From b086f57f56bf0eb9dab9cf321a0f69aaaae84347 Mon Sep 17 00:00:00 2001 From: Andrew Branson Date: Wed, 30 May 2012 08:37:45 +0200 Subject: Initial Maven Conversion --- .../graph/controller/AutoScrollController.java | 41 + .../graph/controller/DeletionController.java | 92 +++ .../controller/EdgeConstructionController.java | 253 ++++++ .../controller/MultiSelectionDragController.java | 571 +++++++++++++ .../graph/controller/StartVertexController.java | 79 ++ .../controller/VertexConstructionController.java | 47 ++ .../com/c2kernel/graph/event/ClearedEvent.java | 6 + .../com/c2kernel/graph/event/EdgeRemovedEvent.java | 6 + .../c2kernel/graph/event/EdgesChangedEvent.java | 6 + .../graph/event/ElasticBandResizedEvent.java | 6 + .../c2kernel/graph/event/ElasticBandSetEvent.java | 6 + .../graph/event/EntireModelChangedEvent.java | 6 + .../c2kernel/graph/event/ForcedNotifyEvent.java | 6 + .../com/c2kernel/graph/event/GraphModelEvent.java | 7 + .../graph/event/GraphModelResizedEvent.java | 6 + .../graph/event/NewEdgeEndPointChangedEvent.java | 6 + .../graph/event/SelectionChangedEvent.java | 9 + .../c2kernel/graph/event/SelectionMovedEvent.java | 7 + .../graph/event/StartVertexIdChangedEvent.java | 6 + .../com/c2kernel/graph/event/VertexAddedEvent.java | 6 + .../c2kernel/graph/event/VertexCreatedEvent.java | 6 + .../com/c2kernel/graph/event/VertexMovedEvent.java | 6 + .../c2kernel/graph/event/VertexRemovedEvent.java | 6 + .../c2kernel/graph/event/VerticesChangedEvent.java | 6 + .../graph/layout/DefaultGraphLayoutGenerator.java | 116 +++ .../com/c2kernel/graph/layout/IntegerWrapper.java | 13 + .../com/c2kernel/graph/model/DirectedEdge.java | 99 +++ .../java/com/c2kernel/graph/model/EdgeFactory.java | 14 + .../java/com/c2kernel/graph/model/ElasticBand.java | 17 + .../java/com/c2kernel/graph/model/GraphModel.java | 900 +++++++++++++++++++++ .../c2kernel/graph/model/GraphModelCastorData.java | 29 + .../c2kernel/graph/model/GraphModelManager.java | 143 ++++ .../java/com/c2kernel/graph/model/GraphPoint.java | 18 + .../java/com/c2kernel/graph/model/Graphable.java | 55 ++ .../com/c2kernel/graph/model/GraphableEdge.java | 71 ++ .../com/c2kernel/graph/model/GraphableVertex.java | 261 ++++++ .../java/com/c2kernel/graph/model/Selection.java | 35 + .../graph/model/TypeNameAndConstructionInfo.java | 24 + src/main/java/com/c2kernel/graph/model/Vertex.java | 308 +++++++ .../com/c2kernel/graph/model/VertexFactory.java | 16 + .../c2kernel/graph/model/VertexOutlineCreator.java | 10 + .../c2kernel/graph/traversal/GraphTraversal.java | 85 ++ .../graph/view/DefaultDirectedEdgeRenderer.java | 75 ++ .../c2kernel/graph/view/DefaultVertexRenderer.java | 60 ++ .../c2kernel/graph/view/DirectedEdgeRenderer.java | 11 + .../c2kernel/graph/view/EditorModeListener.java | 8 + .../java/com/c2kernel/graph/view/EditorPanel.java | 104 +++ .../com/c2kernel/graph/view/EditorToolBar.java | 346 ++++++++ .../java/com/c2kernel/graph/view/GraphPanel.java | 272 +++++++ .../com/c2kernel/graph/view/PropertyTable.java | 40 + .../c2kernel/graph/view/PropertyTableModel.java | 135 ++++ .../c2kernel/graph/view/SelectedVertexPanel.java | 27 + .../c2kernel/graph/view/VertexPropertyPanel.java | 259 ++++++ .../com/c2kernel/graph/view/VertexRenderer.java | 11 + 54 files changed, 4758 insertions(+) create mode 100644 src/main/java/com/c2kernel/graph/controller/AutoScrollController.java create mode 100644 src/main/java/com/c2kernel/graph/controller/DeletionController.java create mode 100644 src/main/java/com/c2kernel/graph/controller/EdgeConstructionController.java create mode 100644 src/main/java/com/c2kernel/graph/controller/MultiSelectionDragController.java create mode 100644 src/main/java/com/c2kernel/graph/controller/StartVertexController.java create mode 100644 src/main/java/com/c2kernel/graph/controller/VertexConstructionController.java create mode 100644 src/main/java/com/c2kernel/graph/event/ClearedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/EdgeRemovedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/EdgesChangedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/ElasticBandResizedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/ElasticBandSetEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/EntireModelChangedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/ForcedNotifyEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/GraphModelEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/GraphModelResizedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/NewEdgeEndPointChangedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/SelectionChangedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/SelectionMovedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/StartVertexIdChangedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/VertexAddedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/VertexCreatedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/VertexMovedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/VertexRemovedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/event/VerticesChangedEvent.java create mode 100644 src/main/java/com/c2kernel/graph/layout/DefaultGraphLayoutGenerator.java create mode 100644 src/main/java/com/c2kernel/graph/layout/IntegerWrapper.java create mode 100644 src/main/java/com/c2kernel/graph/model/DirectedEdge.java create mode 100644 src/main/java/com/c2kernel/graph/model/EdgeFactory.java create mode 100644 src/main/java/com/c2kernel/graph/model/ElasticBand.java create mode 100644 src/main/java/com/c2kernel/graph/model/GraphModel.java create mode 100644 src/main/java/com/c2kernel/graph/model/GraphModelCastorData.java create mode 100644 src/main/java/com/c2kernel/graph/model/GraphModelManager.java create mode 100644 src/main/java/com/c2kernel/graph/model/GraphPoint.java create mode 100644 src/main/java/com/c2kernel/graph/model/Graphable.java create mode 100644 src/main/java/com/c2kernel/graph/model/GraphableEdge.java create mode 100644 src/main/java/com/c2kernel/graph/model/GraphableVertex.java create mode 100644 src/main/java/com/c2kernel/graph/model/Selection.java create mode 100644 src/main/java/com/c2kernel/graph/model/TypeNameAndConstructionInfo.java create mode 100644 src/main/java/com/c2kernel/graph/model/Vertex.java create mode 100644 src/main/java/com/c2kernel/graph/model/VertexFactory.java create mode 100644 src/main/java/com/c2kernel/graph/model/VertexOutlineCreator.java create mode 100644 src/main/java/com/c2kernel/graph/traversal/GraphTraversal.java create mode 100644 src/main/java/com/c2kernel/graph/view/DefaultDirectedEdgeRenderer.java create mode 100644 src/main/java/com/c2kernel/graph/view/DefaultVertexRenderer.java create mode 100644 src/main/java/com/c2kernel/graph/view/DirectedEdgeRenderer.java create mode 100644 src/main/java/com/c2kernel/graph/view/EditorModeListener.java create mode 100644 src/main/java/com/c2kernel/graph/view/EditorPanel.java create mode 100644 src/main/java/com/c2kernel/graph/view/EditorToolBar.java create mode 100644 src/main/java/com/c2kernel/graph/view/GraphPanel.java create mode 100644 src/main/java/com/c2kernel/graph/view/PropertyTable.java create mode 100644 src/main/java/com/c2kernel/graph/view/PropertyTableModel.java create mode 100644 src/main/java/com/c2kernel/graph/view/SelectedVertexPanel.java create mode 100644 src/main/java/com/c2kernel/graph/view/VertexPropertyPanel.java create mode 100644 src/main/java/com/c2kernel/graph/view/VertexRenderer.java (limited to 'src/main/java/com/c2kernel/graph') diff --git a/src/main/java/com/c2kernel/graph/controller/AutoScrollController.java b/src/main/java/com/c2kernel/graph/controller/AutoScrollController.java new file mode 100644 index 0000000..aa04609 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/controller/AutoScrollController.java @@ -0,0 +1,41 @@ +package com.c2kernel.graph.controller; + +import java.awt.Point; +import java.awt.Rectangle; +import java.awt.event.MouseEvent; +import java.awt.event.MouseMotionListener; + +import com.c2kernel.graph.view.GraphPanel; + + +public class AutoScrollController implements MouseMotionListener +{ + private GraphPanel mGraphPanel = null; + + + public void setGraphPanel(GraphPanel graphPanel) + { + mGraphPanel = graphPanel; + mGraphPanel.addMouseMotionListener(this); + } + + + @Override + public void mouseDragged(MouseEvent me) + { + Point mousePoint = null; + + + if(mGraphPanel != null) + { + mousePoint = me.getPoint(); + mGraphPanel.scrollRectToVisible(new Rectangle(mousePoint.x, mousePoint.y, 1, 1)); + } + } + + + @Override + public void mouseMoved(MouseEvent me) + { + } +} diff --git a/src/main/java/com/c2kernel/graph/controller/DeletionController.java b/src/main/java/com/c2kernel/graph/controller/DeletionController.java new file mode 100644 index 0000000..44ea990 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/controller/DeletionController.java @@ -0,0 +1,92 @@ +package com.c2kernel.graph.controller; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.JButton; + +import com.c2kernel.graph.event.SelectionChangedEvent; +import com.c2kernel.graph.model.DirectedEdge; +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.model.Vertex; + + +// The deletion controller is responsible for deleting the present +// selection within the graph. +// +// The controller listens to: +// * The graph model to determine if there is a selection +// * The delete button +// * The graph panel for the typing of the delete key +// +// The controller modifies: +// * The graph model to delete the current selection +// * The delete button to enable it only when there is a selection +public class DeletionController extends KeyAdapter implements Observer, ActionListener +{ + private GraphModelManager mGraphModelManager = null; + private JButton mDeleteButton = null; + + + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mGraphModelManager = graphModelManager; + mGraphModelManager.addObserver(this); + } + + + public void setDeleteButton(JButton deleteButton) + { + mDeleteButton = deleteButton; + mDeleteButton.addActionListener(this); + } + + + // Invoked by the graph model + @Override + public void update(Observable o, Object arg) + { + SelectionChangedEvent event = null; + DirectedEdge selectedEdge = null; + Vertex[] selectedVertices = null; + + + // If the selected edge has changed + if(arg instanceof SelectionChangedEvent && mDeleteButton != null && mGraphModelManager.isEditable()) + { + // Enable the button if a single edge or single vertex is selected + event = (SelectionChangedEvent)arg; + + selectedEdge = event.mSelection.mEdge; + selectedVertices = event.mSelection.mVertices; + + mDeleteButton.setEnabled((selectedEdge != null) || (selectedVertices != null)); + } + } + + + // Invoked by the graph panel + @Override + public void keyPressed(KeyEvent e) + { + if(e.getKeyCode() == KeyEvent.VK_DELETE && mGraphModelManager.isEditable()) + { + mGraphModelManager.getModel().deleteSelection(); + } + } + + + // Invoked by the delete button + @Override + public void actionPerformed(ActionEvent ae) + { + if(mGraphModelManager != null && mGraphModelManager.isEditable()) + { + mGraphModelManager.getModel().deleteSelection(); + } + } +} diff --git a/src/main/java/com/c2kernel/graph/controller/EdgeConstructionController.java b/src/main/java/com/c2kernel/graph/controller/EdgeConstructionController.java new file mode 100644 index 0000000..00ea45b --- /dev/null +++ b/src/main/java/com/c2kernel/graph/controller/EdgeConstructionController.java @@ -0,0 +1,253 @@ +package com.c2kernel.graph.controller; + +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.model.GraphPoint; +import com.c2kernel.graph.model.Vertex; +import com.c2kernel.graph.view.EditorModeListener; +import com.c2kernel.graph.view.EditorToolBar; + + +public class EdgeConstructionController extends MouseAdapter implements EditorModeListener +{ + private GraphModelManager mGraphModelManager = null; + private EditorToolBar mEditorToolBar = null; + + + /**********/ + /* States */ + /**********/ + private final Integer kOtherMode = new Integer(0); + private final Integer kWaitOrigin = new Integer(1); + private final Integer kWaitTerminus = new Integer(2); + + + /**********/ + /* Events */ + /**********/ + private final int kEdgeEntered = 0; + private final int kOtherEntered = 1; + private final int kPressOnVertex = 2; + private final int kDrag = 3; + private final int kReleaseOnTerminus = 4; + private final int kReleaseOnNothing = 5; + + + /***********/ + /* Actions */ + /***********/ + + private interface Action + { + public void doIt(Object data); + } + + + private Action mSetOriginVertex = new Action() + { + @Override + public void doIt(Object data) + { + if(mGraphModelManager != null) + { + mGraphModelManager.getModel().setNewEdgeOriginVertex((Vertex)data); + } + } + }; + + + private Action mSetEndPoint = new Action() + { + @Override + public void doIt(Object data) + { + if(mGraphModelManager != null) + { + mGraphModelManager.getModel().setNewEdgeEndPoint((Point)data); + } + } + }; + + + private Action mClearEdge = new Action() + { + @Override + public void doIt(Object data) + { + if(mGraphModelManager != null) + { + mGraphModelManager.getModel().setNewEdgeOriginVertex(null); + mGraphModelManager.getModel().setNewEdgeEndPoint(null); + } + } + }; + + + private Action mCreateEdge = new Action() + { + @Override + public void doIt(Object data) + { + if((mGraphModelManager != null) && (mEditorToolBar != null) && mGraphModelManager.isEditable()) + { + mGraphModelManager.getModel().createDirectedEdge + ( + mGraphModelManager.getModel().getNewEdgeOriginVertex(), + (Vertex)data, + mEditorToolBar.getSelectedEdgeType() + ); + mGraphModelManager.getModel().setNewEdgeOriginVertex(null); + mGraphModelManager.getModel().setNewEdgeEndPoint(null); + } + } + }; + + + /***********************************/ + /* Finite State Transition Network */ + /***********************************/ + + private Object[][][] mFSTN = + {// OtherMode WaitOrigin WaitTerminus + /* EdgeEntered */ {{null, kWaitOrigin}, null , null }, + /* OtherEntered */ { null , {null , kOtherMode} , null }, + /* PressOnVertex */ { null , {mSetOriginVertex, kWaitTerminus}, null }, + /* Drag */ { null , null , {mSetEndPoint, null} }, + /* ReleaseOnTerminus */ { null , null , {mCreateEdge , kWaitOrigin}}, + /* ReleaseOnNothing */ { null , null , {mClearEdge , kWaitOrigin}} + }; + + + /*****************/ + /* Current state */ + /*****************/ + + private Integer mState = kOtherMode; + + + /**************************/ + /* Event processing logic */ + /**************************/ + + private void processEvent(int event, Object data) + { + Object[] transition = mFSTN[event][mState.intValue()]; + Action action = null; + Integer nextState = null; + + if(transition != null) + { + action = (Action)transition[0]; + nextState = (Integer)transition[1]; + + if(action != null) + { + action.doIt(data); + } + + if(nextState != null) + { + mState = nextState; + } + } + } + + + /********************/ + /* Public interface */ + /********************/ + + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mGraphModelManager = graphModelManager; + } + + + public void setEditorToolBar(EditorToolBar editorToolBar) + { + mEditorToolBar = editorToolBar; + mEditorToolBar.addEditorModeListener(this); + } + + + @Override + public void editorModeChanged(String idOfNewMode) + { + if(idOfNewMode.equals("Edge")) + { + processEvent(kEdgeEntered, null); + } + else + { + processEvent(kOtherEntered, null); + } + } + + + @Override + public void mousePressed(MouseEvent me) + { + Vertex vertex = null; + Point mousePoint = null; + + if(mGraphModelManager != null) + { + // Determine if there is a vertex under the mouse cursor + mousePoint = me.getPoint(); + vertex = mGraphModelManager.getModel().getVertex(new GraphPoint(mousePoint.x, mousePoint.y)); + + // If the mouse has been pressed on a vertex + if(vertex != null) + { + processEvent(kPressOnVertex, vertex); + } + } + } + + + @Override + public void mouseReleased(MouseEvent me) + { + Vertex vertex = null; + Point mousePoint = null; + + if(mGraphModelManager != null) + { + // Determine if there is a vertex under the mouse cursor + mousePoint = me.getPoint(); + vertex = mGraphModelManager.getModel().getVertex(new GraphPoint(mousePoint.x, mousePoint.y)); + + // If the mouse has been released on a vertex which is not the origin vertex + if((vertex != null) && (vertex != mGraphModelManager.getModel().getNewEdgeOriginVertex())) + { + processEvent(kReleaseOnTerminus, vertex); + } + else + { + processEvent(kReleaseOnNothing, null); + } + } + } + + + @Override + public void mouseExited(MouseEvent me) + { + } + + + @Override + public void mouseDragged(MouseEvent me) + { + processEvent(kDrag, me.getPoint()); + } + + + @Override + public void mouseMoved(MouseEvent me) + { + } +} diff --git a/src/main/java/com/c2kernel/graph/controller/MultiSelectionDragController.java b/src/main/java/com/c2kernel/graph/controller/MultiSelectionDragController.java new file mode 100644 index 0000000..eda5c1c --- /dev/null +++ b/src/main/java/com/c2kernel/graph/controller/MultiSelectionDragController.java @@ -0,0 +1,571 @@ +package com.c2kernel.graph.controller; + +import java.awt.Point; +import java.awt.event.InputEvent; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import com.c2kernel.graph.model.DirectedEdge; +import com.c2kernel.graph.model.ElasticBand; +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.model.GraphPoint; +import com.c2kernel.graph.model.GraphableVertex; +import com.c2kernel.graph.model.Selection; +import com.c2kernel.graph.model.Vertex; +import com.c2kernel.graph.view.EditorModeListener; + + +public class MultiSelectionDragController +extends MouseAdapter +implements EditorModeListener, KeyListener +{ + private class ResizeInf + { + public int mMousePressedX = 0; + public int mMousePressedY = 0; + public Vertex mSelectedVertex = null; + public GraphPoint[] mOldOutline = null; + public int mCentreX = 0; + public int mCentreY = 0; + public int mOldHeight = 0; + public int mOldWidth = 0; + } + private ResizeInf mResizeInf = new ResizeInf(); + + private class DispForSelection + { + public int mXDisp = 0; + public int mYDisp = 0; + } + private DispForSelection mDispForSelection = null; + + private class VertexAndDisp + { + public Vertex mVertex = null; + public int mXDisp = 0; + public int mYDisp = 0; + } + + protected GraphModelManager mGraphModelManager = null; + + + /**********/ + /* States */ + /**********/ + protected final Integer kOtherMode = new Integer(0); + protected final Integer kWaiting = new Integer(1); + protected final Integer kResizing = new Integer(2); + protected final Integer kDraggingSelection = new Integer(3); + protected final Integer kStretching = new Integer(4); + + + /**********/ + /* Events */ + /**********/ + protected final int kSelectEntered = 0; + protected final int kOtherEntered = 1; + protected final int kPressOnEdge = 2; + protected final int kPressOnVertex = 3; + protected final int kPressOnSelection = 4; + protected final int kPressOnResizePad = 5; + protected final int kCTRLPressOnVertex = 6; + protected final int kCTRL_A = 7; + protected final int kPressOnNothing = 8; + protected final int kDrag = 9; + protected final int kRelease = 10; + protected final int kZoomIn = 11; + + /***********/ + /* Actions */ + /***********/ + + protected interface Action + { + public void doIt(Object data); + } + + + protected Action mStoreResizeInf = new Action() + { + @Override + public void doIt(Object data) + { + Point mousePoint = (Point)data; + GraphPoint centre = null; + + mResizeInf.mMousePressedX = mousePoint.x; + mResizeInf.mMousePressedY = mousePoint.y; + mResizeInf.mSelectedVertex = mGraphModelManager.getModel().getSelection().mVertices[0]; + mResizeInf.mOldOutline = mResizeInf.mSelectedVertex.getOutlinePoints(); + centre = mResizeInf.mSelectedVertex.getCentrePoint(); + mResizeInf.mCentreX = centre.x; + mResizeInf.mCentreY = centre.y; + mResizeInf.mOldHeight = mResizeInf.mSelectedVertex.getHeight(); + mResizeInf.mOldWidth = mResizeInf.mSelectedVertex.getWidth(); + } + }; + + + protected Action mResizeVertex = new Action() + { + @Override + public void doIt(Object data) + { + Point mousePoint = (Point)data; + int resizeX = 0; + int resizeY = 0; + + + // Calculate how much the old outline should be resized + resizeX = mousePoint.x - mResizeInf.mMousePressedX; + resizeY = mousePoint.y - mResizeInf.mMousePressedY; + + // Clip the resize so that outline does not get any + // smaller than 10 x 10 pixels + if(resizeX < -mResizeInf.mOldWidth/2 + 10) + { + resizeX = -mResizeInf.mOldWidth/2 + 10; + } + + if(resizeY < -mResizeInf.mOldHeight/2 + 10) + { + resizeY = -mResizeInf.mOldHeight/2 + 10; + } + + if (mGraphModelManager.isEditable()) { + mResizeInf.mSelectedVertex.setOutlinePoints(newOutline(resizeX, resizeY)); + mGraphModelManager.forceNotify(); + } + } + + + private GraphPoint[] newOutline(int resizeX, int resizeY) + { + GraphPoint[] newOutline = new GraphPoint[mResizeInf.mOldOutline.length]; + int x = 0; + int y = 0; + int i = 0; + + + for(i=0; i mResizeInf.mCentreX) + { + x = mResizeInf.mOldOutline[i].x + resizeX; + } + else if(mResizeInf.mOldOutline[i].x < mResizeInf.mCentreX) + { + x = mResizeInf.mOldOutline[i].x - resizeX; + } + else + { + x = mResizeInf.mOldOutline[i].x; + } + + if(mResizeInf.mOldOutline[i].y > mResizeInf.mCentreY) + { + y = mResizeInf.mOldOutline[i].y + resizeY; + } + else if(mResizeInf.mOldOutline[i].y < mResizeInf.mCentreY) + { + y = mResizeInf.mOldOutline[i].y - resizeY; + } + else + { + y = mResizeInf.mOldOutline[i].y; + } + + newOutline[i] = new GraphPoint(x, y); + } + + return newOutline; + } + }; + + + protected Action mSelectEdge = new Action() + { + @Override + public void doIt(Object data) + { + Selection selection = new Selection((DirectedEdge)data, null, 0, 0, 0, 0); + + mGraphModelManager.getModel().setSelection(selection); + } + }; + + + protected Action mSelectVertexAndStoreDisp = new Action() + { + @Override + public void doIt(Object data) + { + VertexAndDisp vertexAndDisp = (VertexAndDisp)data; + GraphPoint centrePoint = vertexAndDisp.mVertex.getCentrePoint(); + Selection selection = new Selection(null, + new Vertex[] {vertexAndDisp.mVertex}, + centrePoint.x, + centrePoint.y, + centrePoint.x, + centrePoint.y); + + mGraphModelManager.getModel().setSelection(selection); + mDispForSelection = new DispForSelection(); + mDispForSelection.mXDisp = vertexAndDisp.mXDisp; + mDispForSelection.mYDisp = vertexAndDisp.mYDisp; + } + }; + + + protected Action mStoreDisp = new Action() + { + @Override + public void doIt(Object data) + { + mDispForSelection = (DispForSelection)data; + } + }; + + + protected Action mToggleVertex = new Action() + { + @Override + public void doIt(Object data) + { + Vertex vertex = (Vertex)data; + + if(mGraphModelManager.getModel().inSelection(vertex)) + { + mGraphModelManager.getModel().removeFromSelection(vertex); + } + else + { + mGraphModelManager.getModel().addToSelection(vertex); + } + } + }; + + + protected Action mSelectAll = new Action() + { + @Override + public void doIt(Object data) + { + mGraphModelManager.getModel().selectAll(); + } + }; + + + protected Action mCreateBand = new Action() + { + @Override + public void doIt(Object data) + { + Point fixedCorner = (Point)data; + + mGraphModelManager.getModel().setElasticBand(new ElasticBand(fixedCorner, fixedCorner)); + } + }; + + + protected Action mMoveSelection = new Action() + { + @Override + public void doIt(Object data) + { + Point mousePoint = (Point)data; + int topLeftX = mousePoint.x - mDispForSelection.mXDisp; + int topLeftY = mousePoint.y - mDispForSelection.mYDisp; + if (mGraphModelManager.isEditable()) { + mGraphModelManager.getModel().moveAbsoluteSelection(topLeftX, topLeftY); + } + } + }; + + + protected Action mResizeBand = new Action() + { + @Override + public void doIt(Object data) + { + mGraphModelManager.getModel().resizeElasticBand((Point)data); + } + }; + + + protected Action mSelectContents = new Action() + { + @Override + public void doIt(Object data) + { + mGraphModelManager.getModel().selectContentsOfElasticBand(); + } + }; + + protected Action mZoomIn = new Action() + { + @Override + public void doIt(Object data) // data is the clicked vertex + { + // Need to get child graph model out of the vertex before we can zoom in + if (data instanceof Vertex) { + Vertex zoomTo = (Vertex)data; + if (((GraphableVertex)zoomTo).getIsComposite()) mGraphModelManager.zoomIn(zoomTo); + } + } + }; + + /***********************************/ + /* Finite State Transition Network */ + /***********************************/ + + protected Object[][][] mFSTN = + {// OtherMode Waiting Resizing DraggingSelection Stretching + /* SelectEntered */ {{null, kWaiting}, null , null , null , null }, + /* OtherEntered */ { null , {null , kOtherMode }, null , null , null }, + /* PressOnEdge */ { null , {mSelectEdge , null }, null , null , null }, + /* PressOnVertex */ { null , {mSelectVertexAndStoreDisp, kDraggingSelection}, null , null , null }, + /* PressOnSelection */ { null , {mStoreDisp , kDraggingSelection}, null , null , null }, + /* PressOnResizePad */ { null , {mStoreResizeInf , kResizing }, null , null , null }, + /* CTRLPressOnVertex */ { null , {mToggleVertex , null }, null , null , null }, + /* CTRL_A */ { null , {mSelectAll , null }, null , null , null }, + /* PressOnNothing */ { null , {mCreateBand , kStretching }, null , null , null }, + /* Drag */ { null , null , {mResizeVertex, null }, {mMoveSelection, null }, {mResizeBand , null }}, + /* Release */ { null , null , {null , kWaiting}, {null , kWaiting}, {mSelectContents, kWaiting}}, + /* Double Click */ { null , {mZoomIn , null }, null , null , null }, + }; + + + /*****************/ + /* Current state */ + /*****************/ + + private Integer mState = kWaiting; + + + /**************************/ + /* Event processing logic */ + /**************************/ + + protected void processEvent(int event, Object data) + { + Object[] transition = mFSTN[event][mState.intValue()]; + Action action = null; + Integer nextState = null; + + if(transition != null) + { + action = (Action)transition[0]; + nextState = (Integer)transition[1]; + + if(action != null) + { + action.doIt(data); + } + + if(nextState != null) + { + mState = nextState; + } + } + } + + + /********************/ + /* Public interface */ + /********************/ + + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mGraphModelManager = graphModelManager; + } + + + @Override + public void editorModeChanged(String idOfNewMode) + { + if(idOfNewMode.equals("Select")) + { + processEvent(kSelectEntered, null); + } + else + { + processEvent(kOtherEntered, null); + } + } + + + @Override + public void mousePressed(MouseEvent me) + { + int modifiers = me.getModifiers(); + + if(mGraphModelManager != null) + { + if((modifiers & InputEvent.CTRL_MASK) == 0) + { + mousePressedWithoutCTRL(me.getPoint()); + } + else + { + mousePressedWithCTRL(me.getPoint()); + } + } + } + + @Override + public void mouseClicked(MouseEvent me) + { + if (me.getClickCount() == 2) { // want double click + Point clickedSpot = me.getPoint(); + GraphPoint mouseGPoint = new GraphPoint(clickedSpot.x, clickedSpot.y); + Vertex clicked = mGraphModelManager.getModel().getVertex(mouseGPoint); + if (clicked != null) + processEvent(kZoomIn, clicked); + } + } + + private void mousePressedWithoutCTRL(Point mousePoint) + { + GraphPoint mouseGPoint = new GraphPoint(mousePoint.x, mousePoint.y); + DirectedEdge edge = mGraphModelManager.getModel().getEdge(mouseGPoint); + Vertex vertex = mGraphModelManager.getModel().getVertex(mouseGPoint); + GraphPoint vertexCentrePoint = null; + VertexAndDisp vertexAndDisp = null; + Selection selection = null; + DispForSelection dispForSelection = null; + + + // In order of priority: + // 1. Click on resize pad + // 2. Click on vertex + // 3. Click on edge + if(onResizePad(mouseGPoint)) + { + processEvent(kPressOnResizePad, mousePoint); + } + else if(vertex != null) + { + if(mGraphModelManager.getModel().inSelection(vertex)) + { + selection = mGraphModelManager.getModel().getSelection(); + dispForSelection = new DispForSelection(); + dispForSelection.mXDisp = mousePoint.x - selection.mTopLeftX; + dispForSelection.mYDisp = mousePoint.y - selection.mTopLeftY; + + processEvent(kPressOnSelection, dispForSelection); + } + else + { + vertexCentrePoint = vertex.getCentrePoint(); + + vertexAndDisp = new VertexAndDisp(); + vertexAndDisp.mVertex = vertex; + vertexAndDisp.mXDisp = mousePoint.x - vertexCentrePoint.x; + vertexAndDisp.mYDisp = mousePoint.y - vertexCentrePoint.y; + + processEvent(kPressOnVertex, vertexAndDisp); + } + } + // vertex == null + else + { + if(edge == null) + { + processEvent(kPressOnNothing, mousePoint); + } + else + { + processEvent(kPressOnEdge, edge); + } + } + } + + + private boolean onResizePad(GraphPoint mouseGPoint) + { + Selection selection = mGraphModelManager.getModel().getSelection(); + GraphPoint vertexCentre = null; + int bottomRightX = 0; + int bottomRightY = 0; + + + if(selection.mVertices == null) return false; + + if(selection.mVertices.length == 1) + { + vertexCentre = selection.mVertices[0].getCentrePoint(); + if (vertexCentre == null) return false; + bottomRightX = vertexCentre.x + selection.mVertices[0].getWidth()/2; + bottomRightY = vertexCentre.y + selection.mVertices[0].getHeight()/2; + + return + ( + (mouseGPoint.x > bottomRightX) && + (mouseGPoint.x < bottomRightX + 10) && + (mouseGPoint.y > bottomRightY) && + (mouseGPoint.y < bottomRightY + 10) + ); + } + else + { + return false; + } + } + + + private void mousePressedWithCTRL(Point mousePoint) + { + Vertex vertex = mGraphModelManager.getModel().getVertex(new GraphPoint(mousePoint.x, mousePoint.y)); + + if(vertex != null) + { + processEvent(kCTRLPressOnVertex, vertex); + } + } + + + @Override + public void mouseReleased(MouseEvent me) + { + processEvent(kRelease, null); + } + + + @Override + public void mouseMoved(MouseEvent me) + { + } + + + @Override + public void mouseDragged(MouseEvent e) + { + processEvent(kDrag, e.getPoint()); + } + + + @Override + public void keyPressed(KeyEvent e) + { + if((e.getKeyCode() == KeyEvent.VK_A) && e.isControlDown()) + { + processEvent(kCTRL_A, null); + } + } + + + @Override + public void keyReleased(KeyEvent e) + { + } + + + @Override + public void keyTyped(KeyEvent e) + { + } + +} diff --git a/src/main/java/com/c2kernel/graph/controller/StartVertexController.java b/src/main/java/com/c2kernel/graph/controller/StartVertexController.java new file mode 100644 index 0000000..3984cb7 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/controller/StartVertexController.java @@ -0,0 +1,79 @@ +package com.c2kernel.graph.controller; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.JButton; + +import com.c2kernel.graph.event.SelectionChangedEvent; +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.model.Vertex; + + +// The start vertex controller is responsible for selecting +// the vertex at the start of the graph. +// +// The controller listens to: +// * The graph model to determine if there is a single +// vertex selected +// * The start vertex button +// +// The controller modifies: +// * The graph model to select the start vertex +// * The start button to enable it only when there is a +// single vertex selected +public class StartVertexController implements Observer, ActionListener +{ + private GraphModelManager mGraphModelManager = null; + private JButton mStartButton = null; + + + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mGraphModelManager = graphModelManager; + mGraphModelManager.addObserver(this); + } + + + public void setStartButton(JButton startButton) + { + mStartButton = startButton; + mStartButton.addActionListener(this); + } + + + @Override + public void update(Observable o, Object arg) + { + SelectionChangedEvent event = null; + Vertex[] selectedVertices = null; + + // If the selected vertex has changed + if(arg instanceof SelectionChangedEvent && mStartButton != null) + { + event = (SelectionChangedEvent)arg; + selectedVertices = event.mSelection.mVertices; + + if(selectedVertices == null) + { + mStartButton.setEnabled(false); + } + else if (mGraphModelManager.isEditable()) + { + mStartButton.setEnabled(selectedVertices.length == 1); + } + } + } + + + @Override + public void actionPerformed(ActionEvent ae) + { + if(mGraphModelManager != null) + { + mGraphModelManager.getModel().setSelectedVertexToBeStart(); + } + } +} diff --git a/src/main/java/com/c2kernel/graph/controller/VertexConstructionController.java b/src/main/java/com/c2kernel/graph/controller/VertexConstructionController.java new file mode 100644 index 0000000..1eb91f6 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/controller/VertexConstructionController.java @@ -0,0 +1,47 @@ +package com.c2kernel.graph.controller; + +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; + +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.view.EditorModeListener; +import com.c2kernel.graph.view.EditorToolBar; + + +public class VertexConstructionController extends MouseAdapter implements EditorModeListener +{ + private GraphModelManager mGraphModelManager = null; + private EditorToolBar mEditorToolBar = null; + private boolean mCreatingVertices = false; + + + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mGraphModelManager = graphModelManager; + } + + + public void setEditorToolBar(EditorToolBar editorToolBar) + { + mEditorToolBar = editorToolBar; + mEditorToolBar.addEditorModeListener(this); + } + + + @Override + public void editorModeChanged(String idOfNewMode) + { + mCreatingVertices = idOfNewMode.equals("Vertex"); + } + + + @Override + public void mouseClicked(MouseEvent me) + { + if(mCreatingVertices && (mGraphModelManager != null) && (mEditorToolBar != null) && mGraphModelManager.isEditable()) + { + mGraphModelManager.getModel().createVertex(me.getPoint(), mEditorToolBar.getSelectedVertexType()); + mEditorToolBar.enterSelectMode(); + } + } +} diff --git a/src/main/java/com/c2kernel/graph/event/ClearedEvent.java b/src/main/java/com/c2kernel/graph/event/ClearedEvent.java new file mode 100644 index 0000000..128e671 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/ClearedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class ClearedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/EdgeRemovedEvent.java b/src/main/java/com/c2kernel/graph/event/EdgeRemovedEvent.java new file mode 100644 index 0000000..d895132 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/EdgeRemovedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class EdgeRemovedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/EdgesChangedEvent.java b/src/main/java/com/c2kernel/graph/event/EdgesChangedEvent.java new file mode 100644 index 0000000..7835836 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/EdgesChangedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class EdgesChangedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/ElasticBandResizedEvent.java b/src/main/java/com/c2kernel/graph/event/ElasticBandResizedEvent.java new file mode 100644 index 0000000..cb8ae3e --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/ElasticBandResizedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class ElasticBandResizedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/ElasticBandSetEvent.java b/src/main/java/com/c2kernel/graph/event/ElasticBandSetEvent.java new file mode 100644 index 0000000..6211513 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/ElasticBandSetEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class ElasticBandSetEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/EntireModelChangedEvent.java b/src/main/java/com/c2kernel/graph/event/EntireModelChangedEvent.java new file mode 100644 index 0000000..6e13e8b --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/EntireModelChangedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class EntireModelChangedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/ForcedNotifyEvent.java b/src/main/java/com/c2kernel/graph/event/ForcedNotifyEvent.java new file mode 100644 index 0000000..d8dd646 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/ForcedNotifyEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class ForcedNotifyEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/GraphModelEvent.java b/src/main/java/com/c2kernel/graph/event/GraphModelEvent.java new file mode 100644 index 0000000..95cd6fb --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/GraphModelEvent.java @@ -0,0 +1,7 @@ +package com.c2kernel.graph.event; + +import java.io.Serializable; + +public abstract class GraphModelEvent implements Serializable +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/GraphModelResizedEvent.java b/src/main/java/com/c2kernel/graph/event/GraphModelResizedEvent.java new file mode 100644 index 0000000..20980c3 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/GraphModelResizedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class GraphModelResizedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/NewEdgeEndPointChangedEvent.java b/src/main/java/com/c2kernel/graph/event/NewEdgeEndPointChangedEvent.java new file mode 100644 index 0000000..d1bb6f2 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/NewEdgeEndPointChangedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class NewEdgeEndPointChangedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/SelectionChangedEvent.java b/src/main/java/com/c2kernel/graph/event/SelectionChangedEvent.java new file mode 100644 index 0000000..f95e6d3 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/SelectionChangedEvent.java @@ -0,0 +1,9 @@ +package com.c2kernel.graph.event; + +import com.c2kernel.graph.model.Selection; + + +public class SelectionChangedEvent extends GraphModelEvent +{ + public Selection mSelection = null; +} diff --git a/src/main/java/com/c2kernel/graph/event/SelectionMovedEvent.java b/src/main/java/com/c2kernel/graph/event/SelectionMovedEvent.java new file mode 100644 index 0000000..2d892a9 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/SelectionMovedEvent.java @@ -0,0 +1,7 @@ +package com.c2kernel.graph.event; + + + +public class SelectionMovedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/StartVertexIdChangedEvent.java b/src/main/java/com/c2kernel/graph/event/StartVertexIdChangedEvent.java new file mode 100644 index 0000000..f875626 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/StartVertexIdChangedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class StartVertexIdChangedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/VertexAddedEvent.java b/src/main/java/com/c2kernel/graph/event/VertexAddedEvent.java new file mode 100644 index 0000000..353aacb --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/VertexAddedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class VertexAddedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/VertexCreatedEvent.java b/src/main/java/com/c2kernel/graph/event/VertexCreatedEvent.java new file mode 100644 index 0000000..e72ca4a --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/VertexCreatedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class VertexCreatedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/VertexMovedEvent.java b/src/main/java/com/c2kernel/graph/event/VertexMovedEvent.java new file mode 100644 index 0000000..807ca36 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/VertexMovedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class VertexMovedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/VertexRemovedEvent.java b/src/main/java/com/c2kernel/graph/event/VertexRemovedEvent.java new file mode 100644 index 0000000..e24700d --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/VertexRemovedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class VertexRemovedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/event/VerticesChangedEvent.java b/src/main/java/com/c2kernel/graph/event/VerticesChangedEvent.java new file mode 100644 index 0000000..5d83834 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/event/VerticesChangedEvent.java @@ -0,0 +1,6 @@ +package com.c2kernel.graph.event; + + +public class VerticesChangedEvent extends GraphModelEvent +{ +} diff --git a/src/main/java/com/c2kernel/graph/layout/DefaultGraphLayoutGenerator.java b/src/main/java/com/c2kernel/graph/layout/DefaultGraphLayoutGenerator.java new file mode 100644 index 0000000..8ab91ef --- /dev/null +++ b/src/main/java/com/c2kernel/graph/layout/DefaultGraphLayoutGenerator.java @@ -0,0 +1,116 @@ +package com.c2kernel.graph.layout; + +import java.util.Vector; + +import com.c2kernel.graph.model.DirectedEdge; +import com.c2kernel.graph.model.GraphModel; +import com.c2kernel.graph.model.GraphPoint; +import com.c2kernel.graph.model.Vertex; +import com.c2kernel.utils.Logger; + +public class DefaultGraphLayoutGenerator { + private static int mTopMargin = 100; + private static int mLeftMargin = 100; + private static int mHorzGap = 180; + private static int mVertGap = 100; + + private DefaultGraphLayoutGenerator() { + } + + public static void layoutGraph(GraphModel graphModel) { + Vertex start = graphModel.getStartVertex(); + Vector> rowVector = new Vector>(10, 10); + int[] midPoints = null; + IntegerWrapper valueOfLargestMidPoint = new IntegerWrapper(0); + if (start == null) { + Logger.msg(1,"Error graph must have a starting vertex to be layed out"); + return; + } + graphModel.clearTags(start); + visitVertex(graphModel, start, 0, rowVector, start); + midPoints = new int[rowVector.size()]; + calculateRowMidPoints(rowVector, midPoints, valueOfLargestMidPoint); + fillInVertexLocations(graphModel, rowVector, valueOfLargestMidPoint, midPoints); + fillInEdgeLocations(graphModel); + graphModel.forceNotify(); + } + + private static void visitVertex(GraphModel graphModel, Vertex vertex, int rowIndex, Vector> rowVector, Object tag) { + int i = 0; + Vertex[] children = graphModel.getOutVertices(vertex); + vertex.setTag(tag); + addVertexToRow(vertex, rowIndex, rowVector); + for (i = 0; i < children.length; i++) { + if (!(children[i].hasTag(tag))) { + visitVertex(graphModel, children[i], rowIndex + 1, rowVector, tag); + } + } + } + + private static void addVertexToRow(Vertex vertex, int rowIndex, Vector> rowVector) { + Vector rowsVertices = null; + // If there is no vector of vertices already created for this row, + // then create one + if (rowVector.size() == rowIndex) { + rowVector.add(new Vector(10, 10)); + } + // Add the vertex to the row's vector of vertices + rowsVertices = rowVector.elementAt(rowIndex); + rowsVertices.add(vertex); + } + + private static void calculateRowMidPoints(Vector> rowVector, int[] midPoints, IntegerWrapper valueOfLargestMidPoint) { + Vector rowsVertices = null; + int rowsWidth = 0; + int i = 0; + for (i = 0; i < midPoints.length; i++) { + rowsVertices = rowVector.elementAt(i); + rowsWidth = mHorzGap * (rowsVertices.size() - 1); + midPoints[i] = rowsWidth / 2; + if (midPoints[i] > valueOfLargestMidPoint.mValue) { + valueOfLargestMidPoint.mValue = midPoints[i]; + } + } + } + + private static void fillInVertexLocations(GraphModel graphModel, Vector> rowVector, + IntegerWrapper valueOfLargestMidPoint, int[] midPoints) { + Vector rowsVertices = null; + Vertex vertex = null; + int rowIndex = 0; + int column = 0; + int rowsLeftMargin = 0; + GraphPoint point = new GraphPoint(0, 0); + for (rowIndex = 0; rowIndex < rowVector.size(); rowIndex++) { + rowsVertices = rowVector.elementAt(rowIndex); + rowsLeftMargin = mLeftMargin + valueOfLargestMidPoint.mValue - midPoints[rowIndex]; + for (column = 0; column < rowsVertices.size(); column++) { + vertex = rowsVertices.elementAt(column); + point.x = rowsLeftMargin + column * mHorzGap; + point.y = mTopMargin + rowIndex * mVertGap; + vertex.moveAbsolute(point); + graphModel.checkSize(vertex); + } + } + } + + private static void fillInEdgeLocations(GraphModel graphModel) { + Vertex[] vertices = graphModel.getVertices(); + GraphPoint centrePoint = null; + DirectedEdge[] inEdges = null; + DirectedEdge[] outEdges = null; + int i = 0; + int j = 0; + for (i = 0; i < vertices.length; i++) { + centrePoint = vertices[i].getCentrePoint(); + inEdges = graphModel.getInEdges(vertices[i]); + outEdges = graphModel.getOutEdges(vertices[i]); + for (j = 0; j < inEdges.length; j++) { + inEdges[j].setTerminusPoint(centrePoint); + } + for (j = 0; j < outEdges.length; j++) { + outEdges[j].setOriginPoint(centrePoint); + } + } + } +} diff --git a/src/main/java/com/c2kernel/graph/layout/IntegerWrapper.java b/src/main/java/com/c2kernel/graph/layout/IntegerWrapper.java new file mode 100644 index 0000000..aaee858 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/layout/IntegerWrapper.java @@ -0,0 +1,13 @@ +package com.c2kernel.graph.layout; + + +public class IntegerWrapper +{ + public int mValue = 0; + + + public IntegerWrapper(int value) + { + mValue = value; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/DirectedEdge.java b/src/main/java/com/c2kernel/graph/model/DirectedEdge.java new file mode 100644 index 0000000..830d70d --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/DirectedEdge.java @@ -0,0 +1,99 @@ +package com.c2kernel.graph.model; + +import java.io.Serializable; + + + +public abstract class DirectedEdge implements Serializable +{ + // Persistent data + private int mId = -1; + private GraphPoint mOriginPoint = new GraphPoint(0, 0); + private GraphPoint mTerminusPoint = new GraphPoint(0, 0); + private int mOriginVertexId = -1; + private int mTerminusVertexId = -1; + + + public void setID(int id) + { + mId = id; + } + + + public int getID() + { + return mId; + } + + + public void setOriginPoint(GraphPoint p) + { + mOriginPoint = p; + } + + + public GraphPoint getOriginPoint() + { + return mOriginPoint; + } + + + public void setTerminusPoint(GraphPoint p) + { + mTerminusPoint = p; + } + + + public GraphPoint getTerminusPoint() + { + return mTerminusPoint; + } + + + public boolean containsPoint(GraphPoint p) + { + int midX = mOriginPoint.x + (mTerminusPoint.x - mOriginPoint.x)/2; + int midY = mOriginPoint.y + (mTerminusPoint.y - mOriginPoint.y)/2; + int minX = midX - 10; + int minY = midY - 10; + int maxX = midX + 10; + int maxY = midY + 10; + + return (p.x >= minX) && (p.x <= maxX) && (p.y >= minY) && (p.y <= maxY); + } + + + public void setOriginVertexId(int id) + { + mOriginVertexId = id; + } + + + public int getOriginVertexId() + { + return mOriginVertexId; + } + + + public void setTerminusVertexId(int id) + { + mTerminusVertexId = id; + } + + + public int getTerminusVertexId() + { + return mTerminusVertexId; + } + + + public void setName(String name) + { + } + + + public String getName() + { + return null; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/EdgeFactory.java b/src/main/java/com/c2kernel/graph/model/EdgeFactory.java new file mode 100644 index 0000000..083f616 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/EdgeFactory.java @@ -0,0 +1,14 @@ +package com.c2kernel.graph.model; + + + +public interface EdgeFactory +{ + public void create + ( + GraphModelManager graphModelManager, + Vertex origin, + Vertex terminus, + TypeNameAndConstructionInfo typeNameAndConstructionInfo + ); +} diff --git a/src/main/java/com/c2kernel/graph/model/ElasticBand.java b/src/main/java/com/c2kernel/graph/model/ElasticBand.java new file mode 100644 index 0000000..38497ef --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/ElasticBand.java @@ -0,0 +1,17 @@ +package com.c2kernel.graph.model; + +import java.awt.Point; + + +public class ElasticBand +{ + public Point mFixedCorner = null; + public Point mMovingCorner = null; + + + public ElasticBand(Point fixedCorner, Point movingCorner) + { + mFixedCorner = fixedCorner; + mMovingCorner = movingCorner; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/GraphModel.java b/src/main/java/com/c2kernel/graph/model/GraphModel.java new file mode 100644 index 0000000..47f368d --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/GraphModel.java @@ -0,0 +1,900 @@ +package com.c2kernel.graph.model; + +import java.awt.Point; +import java.awt.Polygon; +import java.io.Serializable; +import java.util.Hashtable; +import java.util.Vector; + +import com.c2kernel.graph.event.ClearedEvent; +import com.c2kernel.graph.event.EdgeRemovedEvent; +import com.c2kernel.graph.event.EdgesChangedEvent; +import com.c2kernel.graph.event.ElasticBandResizedEvent; +import com.c2kernel.graph.event.ElasticBandSetEvent; +import com.c2kernel.graph.event.ForcedNotifyEvent; +import com.c2kernel.graph.event.GraphModelEvent; +import com.c2kernel.graph.event.GraphModelResizedEvent; +import com.c2kernel.graph.event.NewEdgeEndPointChangedEvent; +import com.c2kernel.graph.event.SelectionChangedEvent; +import com.c2kernel.graph.event.SelectionMovedEvent; +import com.c2kernel.graph.event.StartVertexIdChangedEvent; +import com.c2kernel.graph.event.VertexAddedEvent; +import com.c2kernel.graph.event.VertexCreatedEvent; +import com.c2kernel.graph.event.VertexMovedEvent; +import com.c2kernel.graph.event.VertexRemovedEvent; +import com.c2kernel.graph.event.VerticesChangedEvent; +import com.c2kernel.utils.Logger; + +public class GraphModel implements Serializable{ + /* Persistant data */ + + private int mWidth = 0; + private int mHeight = 0; + private int mNextId = 0; + protected int mStartVertexId = -1; + protected Hashtable mVertexHashtable = new Hashtable(); + protected Hashtable mEdgeHashtable = new Hashtable(); + private GraphableVertex mContainingVertex; + + /* Transient data */ + + // There should always be a Selection object + protected Selection mSelection = new Selection(null, null, 0, 0, 0, 0); + protected Vertex mNewEdgeOriginVertex = null; + protected Point mNewEdgeEndPoint = null; + private ElasticBand mElasticBand = null; + private GraphModelManager mManager = null; + + /* External factories */ + + private VertexFactory mExternalVertexFactory = null; + private EdgeFactory mExternalEdgeFactory = null; + + /* Vertex outline creator */ + + private VertexOutlineCreator mVertexOutlineCreator = null; + + /* Notification Events */ + + private ClearedEvent mClearedEvent = new ClearedEvent(); + private EdgeRemovedEvent mEdgeRemovedEvent = new EdgeRemovedEvent(); + private EdgesChangedEvent mEdgesChangedEvent = new EdgesChangedEvent(); + private ForcedNotifyEvent mForcedNotifyEvent = new ForcedNotifyEvent(); + private NewEdgeEndPointChangedEvent mNewEdgeEndPointChangedEvent = new NewEdgeEndPointChangedEvent(); + private SelectionChangedEvent mSelectionChangedEvent = new SelectionChangedEvent(); + private StartVertexIdChangedEvent mStartVertexIdChangedEvent = new StartVertexIdChangedEvent(); + private VertexAddedEvent mVertexAddedEvent = new VertexAddedEvent(); + private VertexCreatedEvent mVertexCreatedEvent = new VertexCreatedEvent(); + private VertexMovedEvent mVertexMovedEvent = new VertexMovedEvent(); + private SelectionMovedEvent mSelectionMovedEvent = new SelectionMovedEvent(); + private VertexRemovedEvent mVertexRemovedEvent = new VertexRemovedEvent(); + private VerticesChangedEvent mVerticesChangedEvent = new VerticesChangedEvent(); + private ElasticBandSetEvent mElasticBandSetEvent = new ElasticBandSetEvent(); + private ElasticBandResizedEvent mElasticBandResizedEvent = new ElasticBandResizedEvent(); + private GraphModelResizedEvent mGraphModelResizedEvent = new GraphModelResizedEvent(); + + // Calling this constructor does not create a vertex outline creator + // which is required by the method addVertexAndCreateId() + + private static int count=0; + + // count instances for debugging + private int number; + + public GraphModel() { + number=++count; + + } + + public int getNumber() { + return number; + } + + private void setChanged() { + if (mManager != null) + mManager.setChanged(); + } + + private void notifyObservers(GraphModelEvent ev) { + if (mManager != null) + mManager.notifyObservers(ev); + } + + public void setNextId(int id) { + mNextId = id; + } + + public int getNextId() { + return mNextId; + } + + public void setManager(GraphModelManager mgr) { + mManager = mgr; + } + + public GraphModelManager getManager() { + return mManager; + } + + public GraphModel(VertexOutlineCreator vertexOutlineCreator) { + mVertexOutlineCreator = vertexOutlineCreator; + } + + public void setWidth(int width) { + + mWidth = width; + } + + public int getWidth() { + return mWidth; + } + + public void setHeight(int height) { + + mHeight = height; + } + + public int getHeight() { + return mHeight; + } + + public void checkSize(Vertex v) { + boolean resized = false; + GraphPoint centre = v.getCentrePoint(); + if (getWidth() < centre.x + v.getWidth()/2 +10 ) { + setWidth( centre.x + v.getWidth()/2 +10 ); + resized = true; + } + + if (getHeight() < centre.y + v.getHeight()/2 +10 ) { + setHeight(centre.y + v.getHeight()/2 +10 ); + resized = true; + } + + if (resized) { + setChanged(); + notifyObservers(mGraphModelResizedEvent); + } + + } + + public void setStartVertexId(int id) { + mStartVertexId = id; + } + + public int getStartVertexId() { + return mStartVertexId; + } + + public Vertex getStartVertex() { + return resolveVertex(getStartVertexId()); + } + + /** + * @return Returns the mParentVertex. + */ + public GraphableVertex getContainingVertex() { + return mContainingVertex; + } + /** + * @param parentVertex The mParentVertex to set. + */ + public void setContainingVertex(GraphableVertex vertex) { + mContainingVertex = vertex; + } + + public void setVertices(Vertex[] vertices) { + mVertexHashtable = new Hashtable(); + for (Vertex vertice : vertices) { + mVertexHashtable.put(String.valueOf(vertice.getID()), vertice); + checkSize(vertice); + + } + setChanged(); + notifyObservers(mVerticesChangedEvent); + } + + public Vertex[] getVertices() { + Object[] vertexObjs = mVertexHashtable.values().toArray(); + Vertex[] vertices = new Vertex[vertexObjs.length]; + int i = 0; + for (i = 0; i < vertices.length; i++) { + vertices[i] = (Vertex)vertexObjs[i]; + } + return vertices; + } + + public void setEdges(DirectedEdge[] edges) { + mEdgeHashtable = new Hashtable(); + for (DirectedEdge edge : edges) { + mEdgeHashtable.put(String.valueOf(edge.getID()), edge); + } + setChanged(); + notifyObservers(mEdgesChangedEvent); + } + + public DirectedEdge[] getEdges() { + Object[] edgeObjs = mEdgeHashtable.values().toArray(); + DirectedEdge[] edges = new DirectedEdge[edgeObjs.length]; + int i = 0; + for (i = 0; i < edges.length; i++) { + edges[i] = (DirectedEdge)edgeObjs[i]; + } + return edges; + } + + // If the specified point is within more than one vertex, + // then the smallest vertex is returned. + public Vertex getVertex(GraphPoint p) { + Object[] vertexObjs = mVertexHashtable.values().toArray(); + Vertex vertex = null; + Vector vertexVector = new Vector(10, 10); + int numVerticesFound = 0; + Vertex smallestVertex = null; + int sizeOfSmallestVertex = 0; + int sizeOfVertex = 0; + int i = 0; + for (i = 0; i < vertexObjs.length; i++) { + vertex = (Vertex)vertexObjs[i]; + if (vertex.containsPoint(p)) { + vertexVector.add(vertex); + } + } + numVerticesFound = vertexVector.size(); + if (numVerticesFound == 0) { + return null; + } + else { + smallestVertex = vertexVector.elementAt(0); + sizeOfSmallestVertex = smallestVertex.getHeight() * smallestVertex.getWidth(); + // Determine the smallest vertex + for (i = 1; i < numVerticesFound; i++) { + vertex = vertexVector.elementAt(i); + sizeOfVertex = vertex.getHeight() * vertex.getWidth(); + if (sizeOfVertex < sizeOfSmallestVertex) { + smallestVertex = vertex; + sizeOfSmallestVertex = sizeOfVertex; + } + } + return smallestVertex; + } + } + + public Vertex getVertexById(int id) { + return mVertexHashtable.get(String.valueOf(id)); + } + + public DirectedEdge getEdge(GraphPoint p) { + Object[] edgeObjs = mEdgeHashtable.values().toArray(); + DirectedEdge edge = null; + int i = 0; + for (i = 0; i < edgeObjs.length; i++) { + edge = (DirectedEdge)edgeObjs[i]; + if (edge.containsPoint(p)) { + return edge; + } + } + return null; + } + + public int addEdgeAndCreateId(DirectedEdge e, int originId, int terminusId) { + return addEdgeAndCreateId(e, resolveVertex(originId), resolveVertex(terminusId)); + } + + public int addEdgeAndCreateId(DirectedEdge e, Vertex origin, Vertex terminus) { + e.setID(mNextId); + e.setOriginVertexId(origin.getID()); + e.setOriginPoint(origin.getCentrePoint()); + e.setTerminusVertexId(terminus.getID()); + e.setTerminusPoint(terminus.getCentrePoint()); + origin.addOutEdgeId(mNextId); + terminus.addInEdgeId(mNextId); + mEdgeHashtable.put(String.valueOf(mNextId), e); + mNextId++; + return mNextId - 1; + } + + // Removes an edge, but does not modify the selection + public void removeEdge(DirectedEdge e) { + Vertex origin = getOrigin(e); + Vertex terminus = getTerminus(e); + int edgeId = e.getID(); + // Remove the id of the edge from the origin and terminus vertices + origin.removeOutEdgeId(edgeId); + terminus.removeInEdgeId(edgeId); + // Remove the edge + mEdgeHashtable.remove(String.valueOf(e.getID())); + setChanged(); + notifyObservers(mEdgeRemovedEvent); + } + + public int addVertexAndCreateId(Vertex v, Point location) { + return addVertexAndCreateId(v, new GraphPoint(location.x, location.y)); + } + + public int addVertexAndCreateId(Vertex v, GraphPoint location) { + if (location!= null) + { + if (mVertexOutlineCreator == null) { + Logger.msg(1,"You cannot add a vertex with no outline creator"); + return -1; + + } + mVertexHashtable.put(String.valueOf(mNextId), v); + placeVertex(v, location); + } + v.setID(mNextId); + return mNextId++; + } + + public void placeVertex(Vertex v, GraphPoint location) { + v.setCentrePoint(location); + if (mVertexOutlineCreator != null) { + mVertexOutlineCreator.setOutline(v); + } + setChanged(); + notifyObservers(mVertexAddedEvent); + checkSize(v); + } + + // Removes a vertex, but does not modify the selection + public void removeVertex(Vertex v) { + DirectedEdge[] inEdges = getInEdges(v); + DirectedEdge[] outEdges = getOutEdges(v); + Vertex origin = null; + Vertex terminus = null; + int edgeId = -1; + int i = 0; + // For each in edge + for (i = 0; i < inEdges.length; i++) { + edgeId = inEdges[i].getID(); + origin = getOrigin(inEdges[i]); + // Remove the id of the edge from the origin vertex + origin.removeOutEdgeId(edgeId); + // Remove the edge + mEdgeHashtable.remove(String.valueOf(edgeId)); + } + // Remove all the out edges + for (i = 0; i < outEdges.length; i++) { + edgeId = outEdges[i].getID(); + terminus = getTerminus(outEdges[i]); + // Remove the id of the edge from the terminus vertex + terminus.removeInEdgeId(edgeId); + // Remove the edge + mEdgeHashtable.remove(String.valueOf(edgeId)); + } + // Remove the vertex + mVertexHashtable.remove(String.valueOf(v.getID())); + setChanged(); + notifyObservers(mVertexRemovedEvent); + } + + public void moveAbsoluteVertex(Vertex v, GraphPoint p) { + // Make sure the new position stays within the graph + if (p.x < 0) p.x = 0; + if (p.y < 0) p.y = 0; + if (p.x > mWidth) p.x = mWidth; + if (p.y > mHeight) p.y = mHeight; + moveAbsoluteVertexAndConnectingEdges(v, p); + setChanged(); + notifyObservers(mVertexMovedEvent); + } + + private void moveAbsoluteVertexAndConnectingEdges(Vertex v, GraphPoint p) { + DirectedEdge[] inEdges = getInEdges(v); + DirectedEdge[] outEdges = getOutEdges(v); + int i = 0; + // Move the vertex to the new position + v.moveAbsolute(p); + // Move the ends of the incoming edges to the new position + for (i = 0; i < inEdges.length; i++) { + inEdges[i].setTerminusPoint(p); + } + // Move the ends of the outgoing edges to the new position + for (i = 0; i < outEdges.length; i++) { + outEdges[i].setOriginPoint(p); + } + checkSize(v); + } + + public void moveAbsoluteSelection(int newTopLeftX, int newTopLeftY) { + int selectionHeight = mSelection.mBottomRightY - mSelection.mTopLeftY; + int selectionWidth = mSelection.mBottomRightX - mSelection.mTopLeftX; + int bottomRightX = newTopLeftX + selectionWidth; + int bottomRightY = newTopLeftY + selectionHeight; + GraphPoint oldCentrePoint = null; + GraphPoint newCentrePoint = null; + int distXFromTopLeft = 0; + int distYFromTopLeft = 0; + int i = 0; + // Make sure the selection does not move + // outside the boundaries of the graph + if (newTopLeftX < 0) newTopLeftX = 0; + if (newTopLeftY < 0) newTopLeftY = 0; + if (bottomRightX > mWidth) newTopLeftX = mWidth - selectionWidth; + if (bottomRightY > mHeight) newTopLeftY = mHeight - selectionHeight; + // For each selected vertex + for (i = 0; i < mSelection.mVertices.length; i++) { + // Calculate the new centre point of the vertex. + // First calculate the distance of the centre point + // from the old top left hand corner of the selection, + // then move the point to the new top left hand + // corner plus the distance. + oldCentrePoint = mSelection.mVertices[i].getCentrePoint(); + distXFromTopLeft = oldCentrePoint.x - mSelection.mTopLeftX; + distYFromTopLeft = oldCentrePoint.y - mSelection.mTopLeftY; + newCentrePoint = new GraphPoint(newTopLeftX + distXFromTopLeft, newTopLeftY + distYFromTopLeft); + moveAbsoluteVertexAndConnectingEdges(mSelection.mVertices[i], newCentrePoint); + } + // Update the top left and bottom right corners + mSelection.mTopLeftX = newTopLeftX; + mSelection.mTopLeftY = newTopLeftY; + mSelection.mBottomRightX = newTopLeftX + selectionWidth; + mSelection.mBottomRightY = newTopLeftY + selectionHeight; + setChanged(); + notifyObservers(mSelectionMovedEvent); + } + + public Vertex resolveVertex(int id) { + return mVertexHashtable.get(String.valueOf(id)); + } + + public DirectedEdge resolveEdge(int id) { + return mEdgeHashtable.get(String.valueOf(id)); + } + + public DirectedEdge[] getInEdges(Vertex v) { + int[] ids = v.getInEdgeIds(); + return resolveEdges(ids); + } + + public DirectedEdge[] getOutEdges(Vertex v) { + int[] ids = v.getOutEdgeIds(); + return resolveEdges(ids); + } + + private DirectedEdge[] resolveEdges(int[] ids) { + DirectedEdge[] edges = new DirectedEdge[ids.length]; + int i = 0; + for (i = 0; i < ids.length; i++) { + edges[i] = resolveEdge(ids[i]); + } + return edges; + } + + public Vertex getOrigin(DirectedEdge e) { + return resolveVertex(e.getOriginVertexId()); + } + + public Vertex getTerminus(DirectedEdge e) { + return resolveVertex(e.getTerminusVertexId()); + } + + public Vertex[] getInVertices(Vertex v) { + DirectedEdge[] inEdges = getInEdges(v); + Vertex[] inVertices = new Vertex[inEdges.length]; + int i = 0; + for (i = 0; i < inEdges.length; i++) { + inVertices[i] = getOrigin(inEdges[i]); + } + return inVertices; + } + + public Vertex[] getOutVertices(Vertex v) { + DirectedEdge[] outEdges = getOutEdges(v); + Vertex[] outVertices = new Vertex[outEdges.length]; + int i = 0; + for (i = 0; i < outEdges.length; i++) { + outVertices[i] = getTerminus(outEdges[i]); + } + return outVertices; + } + + public DirectedEdge[] getConnectingEdges(int originVertexId, int terminusVertexId) { + Vertex origin = resolveVertex(originVertexId); + DirectedEdge[] outEdges = null; + int numEdgesFound = 0; + DirectedEdge[] edgesFound = null; + int i = 0; + int j = 0; + if (origin == null) return null; + outEdges = getOutEdges(origin); + for (i = 0; i < outEdges.length; i++) { + if (outEdges[i].getTerminusVertexId() == terminusVertexId) { + numEdgesFound++; + } + } + edgesFound = new DirectedEdge[numEdgesFound]; + for (i = 0; i < outEdges.length; i++) { + if (outEdges[i].getTerminusVertexId() == terminusVertexId) { + edgesFound[j] = outEdges[i]; + j++; + } + } + return edgesFound; + } + + public void clearTags(Object tag) { + Vertex vertex = null; + Object[] vertexObjs = mVertexHashtable.values().toArray(); + int i = 0; + for (i = 0; i < vertexObjs.length; i++) { + vertex = (Vertex)vertexObjs[i]; + vertex.clearTag(tag); + } + } + + public void forceNotify() { + setChanged(); + notifyObservers(mForcedNotifyEvent); + } + + public void clear() { + mVertexHashtable = new Hashtable(); + mEdgeHashtable = new Hashtable(); + mStartVertexId = -1; + setChanged(); + notifyObservers(mClearedEvent); + } + + public void setSelection(Selection s) { + // If the there is a change + if (selectionChanged(s)) { + mSelection = s; + mSelectionChangedEvent.mSelection = s; + setChanged(); + notifyObservers(mSelectionChangedEvent); + } + } + + private boolean selectionChanged(Selection newValue) { + int i = 0; + if (mSelection.mEdge != newValue.mEdge) { + return true; + } + if (mSelection.mVertices == null) { + if (newValue.mVertices == null) { + return false; + } + else { + return true; + } + } + else { + if (newValue.mVertices == null) { + return true; + } + else { + if (mSelection.mVertices.length != newValue.mVertices.length) { + return true; + } + for (i = 0; i < mSelection.mVertices.length; i++) { + if (mSelection.mVertices[i] != newValue.mVertices[i]) { + return true; + } + } + return false; + } + } + } + + public Selection getSelection() { + return mSelection; + } + + public void setNewEdgeOriginVertex(Vertex v) { + mNewEdgeOriginVertex = v; + } + + public Vertex getNewEdgeOriginVertex() { + return mNewEdgeOriginVertex; + } + + public void setNewEdgeEndPoint(Point p) { + mNewEdgeEndPoint = p; + setChanged(); + notifyObservers(mNewEdgeEndPointChangedEvent); + } + + public Point getNewEdgeEndPoint() { + return mNewEdgeEndPoint; + } + + public void setExternalVertexFactory(VertexFactory factory) { + mExternalVertexFactory = factory; + } + + public void createVertex(Point location, TypeNameAndConstructionInfo typeNameAndConstructionInfo) { + if (mExternalVertexFactory != null) { + mExternalVertexFactory.create(mManager, location, typeNameAndConstructionInfo); + setChanged(); + notifyObservers(mVertexCreatedEvent); + } + } + + public void setExternalEdgeFactory(EdgeFactory factory) { + mExternalEdgeFactory = factory; + } + + public void setVertexOutlineCreator(VertexOutlineCreator outlineCreator) { + mVertexOutlineCreator = outlineCreator; + } + + public void createDirectedEdge(Vertex origin, Vertex terminus, TypeNameAndConstructionInfo typeNameAndConstructionInfo) { + if (mExternalEdgeFactory != null) { + mExternalEdgeFactory.create(mManager, origin, terminus, typeNameAndConstructionInfo); + } + } + + public void selectAll() { + Vertex[] allVertices = getVertices(); + if (allVertices.length > 0) { + mSelection.mEdge = null; + mSelection.mVertices = allVertices; + updateSelectionCorners(); + mSelectionChangedEvent.mSelection = mSelection; + setChanged(); + notifyObservers(mSelectionChangedEvent); + } + } + + public void selectContentsOfElasticBand() { + if (mElasticBand == null) return; + Polygon bandPolygon = new Polygon(); + Vertex[] allVertices = getVertices(); + GraphPoint centrePoint = null; + Vector verticesInside = new Vector(10, 10); + int i = 0; + // Create a polygon representing the elastic band + bandPolygon.addPoint(mElasticBand.mFixedCorner.x, mElasticBand.mFixedCorner.y); + bandPolygon.addPoint(mElasticBand.mMovingCorner.x, mElasticBand.mFixedCorner.y); + bandPolygon.addPoint(mElasticBand.mMovingCorner.x, mElasticBand.mMovingCorner.y); + bandPolygon.addPoint(mElasticBand.mFixedCorner.x, mElasticBand.mMovingCorner.y); + // Create a vector of all of the vertices within the elastic band polygon + for (i = 0; i < allVertices.length; i++) { + centrePoint = allVertices[i].getCentrePoint(); + if (bandPolygon.contains(centrePoint.x, centrePoint.y)) { + verticesInside.add(allVertices[i]); + } + } + + // Select the vertices found within the elastic band polygon + if (verticesInside.size() == 0) { + mSelection.mTopLeftX = 0; + mSelection.mTopLeftY = 0; + mSelection.mBottomRightX = 0; + mSelection.mBottomRightY = 0; + mSelection.mEdge = null; + if (mContainingVertex != null) + verticesInside.add(mContainingVertex); + else + mSelection.mVertices = null; + } + + if (verticesInside.size() > 0) { + mSelection.mEdge = null; + mSelection.mVertices = new Vertex[verticesInside.size()]; + for (i = 0; i < verticesInside.size(); i++) { + mSelection.mVertices[i] = verticesInside.elementAt(i); + } + updateSelectionCorners(); + } + // Remove the elastic band + mElasticBand = null; + mSelectionChangedEvent.mSelection = mSelection; + setChanged(); + notifyObservers(mSelectionChangedEvent); + } + + // Updates the top left and bottom right corners of the selection + private void updateSelectionCorners() { + Vertex vertex = mSelection.mVertices[0]; + GraphPoint centrePoint = vertex.getCentrePoint(); + if (centrePoint == null) return; + mSelection.mTopLeftX = centrePoint.x; + mSelection.mTopLeftY = centrePoint.y; + mSelection.mBottomRightX = centrePoint.x; + mSelection.mBottomRightY = centrePoint.y; + for (Vertex mVertice : mSelection.mVertices) { + vertex = mVertice; + centrePoint = vertex.getCentrePoint(); + if (centrePoint.x < mSelection.mTopLeftX) { + mSelection.mTopLeftX = centrePoint.x; + } + if (centrePoint.y < mSelection.mTopLeftY) { + mSelection.mTopLeftY = centrePoint.y; + } + if (centrePoint.x > mSelection.mBottomRightX) { + mSelection.mBottomRightX = centrePoint.x; + } + if (centrePoint.y > mSelection.mBottomRightY) { + mSelection.mBottomRightY = centrePoint.y; + } + } + } + + public void deleteSelection() { + int i = 0; + if (mSelection.mVertices != null) { + for (i = 0; i < mSelection.mVertices.length; i++) { + removeVertex(mSelection.mVertices[i]); + } + } + else if (mSelection.mEdge != null) { + removeEdge(mSelection.mEdge); + } + // Make sure nothing is selected + if ((mSelection.mEdge != null) || (mSelection.mVertices != null)) { + mSelection.mEdge = null; + mSelection.mVertices = null; + mSelectionChangedEvent.mSelection = mSelection; + setChanged(); + notifyObservers(mSelectionChangedEvent); + } + } + + public void setSelectedVertexToBeStart() { + if (mSelection.mVertices != null) { + if (mSelection.mVertices.length == 1) { + setStartVertexId(mSelection.mVertices[0].getID()); + setChanged(); + notifyObservers(mStartVertexIdChangedEvent); + } + } + } + + public void setElasticBand(ElasticBand elasticBand) { + mElasticBand = elasticBand; + setChanged(); + notifyObservers(mElasticBandSetEvent); + } + + public ElasticBand getElasticBand() { + return mElasticBand; + } + + public void resizeElasticBand(Point movingCorner) { + mElasticBand.mMovingCorner = movingCorner; + setChanged(); + notifyObservers(mElasticBandResizedEvent); + } + + public boolean inSelection(Vertex v) { + int i = 0; + if (mSelection.mVertices == null) { + return false; + } + else { + for (i = 0; i < mSelection.mVertices.length; i++) { + if (mSelection.mVertices[i] == v) { + return true; + } + } + return false; + } + } + + // Only use this method to remove one vertex. + // If you wish to remove more, it would + // propably be more efficient to create a + // new Selection object. + public void removeFromSelection(Vertex v) { + Vertex[] vertices = null; + int i = 0; + int j = 0; + if (mSelection.mVertices.length == 1) { + mSelection.mVertices = null; + mSelection.mTopLeftX = 0; + mSelection.mTopLeftY = 0; + mSelection.mBottomRightX = 0; + mSelection.mBottomRightY = 0; + } + else { + vertices = new Vertex[mSelection.mVertices.length - 1]; + for (i = 0; i < mSelection.mVertices.length; i++) { + if (mSelection.mVertices[i] != v) { + vertices[j] = mSelection.mVertices[i]; + j++; + } + } + mSelection.mVertices = vertices; + updateSelectionCorners(); + } + setChanged(); + notifyObservers(mSelectionChangedEvent); + } + + // Only use this method to add one vertex. + // If you wish to add more, it would + // propably be more efficient to create a + // new Selection object. + public void addToSelection(Vertex v) { + Vertex[] vertices = new Vertex[mSelection.mVertices.length + 1]; + GraphPoint centrePoint = null; + int i = 0; + if (mSelection.mVertices == null) { + centrePoint = v.getCentrePoint(); + mSelection.mVertices = new Vertex[] { v }; + mSelection.mTopLeftX = centrePoint.x; + mSelection.mTopLeftY = centrePoint.y; + mSelection.mBottomRightX = centrePoint.x; + mSelection.mBottomRightY = centrePoint.y; + } + else { + for (i = 0; i < mSelection.mVertices.length; i++) { + vertices[i] = mSelection.mVertices[i]; + } + vertices[mSelection.mVertices.length] = v; + mSelection.mVertices = vertices; + updateSelectionCorners(); + } + setChanged(); + notifyObservers(mSelectionChangedEvent); + } + + public void resetVertexOutlines() { + Vertex[] vertices = getVertices(); + int i = 0; + for (i = 0; i < vertices.length; i++) { + mVertexOutlineCreator.setOutline(vertices[i]); + } + } + + public void setGraphModelCastorData(GraphModelCastorData data) { + Class vertexOutlineCreatorClass = null; + int i = 0; + // Create the vertex outline creator + if (data.mClassNameOfVertexOutlineCreator.equals("")) { + mVertexOutlineCreator = null; + } + else { + try { + vertexOutlineCreatorClass = Class.forName(data.mClassNameOfVertexOutlineCreator); + mVertexOutlineCreator = (VertexOutlineCreator)vertexOutlineCreatorClass.newInstance(); + } + catch (Exception e) { + e.printStackTrace(); + mVertexOutlineCreator = null; + } + } + // Create and populate the vertex hashtable + mVertexHashtable = new Hashtable(); + for (i = 0; i < data.mVertexImpls.length; i++) { + mVertexHashtable.put(String.valueOf(data.mVertexImpls[i].getID()), data.mVertexImpls[i]); + checkSize(data.mVertexImpls[i]); + } + // Create and populate the edge hastable + mEdgeHashtable = new Hashtable(); + for (i = 0; i < data.mEdgeImpls.length; i++) { + mEdgeHashtable.put(String.valueOf(data.mEdgeImpls[i].getID()), data.mEdgeImpls[i]); + } + // Set the start vertex id and the id generation counter + mStartVertexId = data.mStartVertexId; + mNextId = data.mNextId; + } + + public GraphModelCastorData getGraphModelCastorData() { + Object[] vertexObjs = mVertexHashtable.values().toArray(); + Vertex[] vertexImpls = new Vertex[vertexObjs.length]; + Object[] edgeObjs = mEdgeHashtable.values().toArray(); + DirectedEdge[] directedEdgeImpls = new DirectedEdge[edgeObjs.length]; + String className = null; + int i = 0; + // Put in the vertices + for (i = 0; i < vertexImpls.length; i++) { + vertexImpls[i] = (Vertex)vertexObjs[i]; + } + // Put in the edges + for (i = 0; i < directedEdgeImpls.length; i++) { + directedEdgeImpls[i] = (DirectedEdge)edgeObjs[i]; + } + // Determine the class name of the vertex outline creator + if (mVertexOutlineCreator == null) { + className = ""; + } + else { + className = mVertexOutlineCreator.getClass().getName(); + } + return new GraphModelCastorData(className, vertexImpls, directedEdgeImpls, mStartVertexId, mNextId); + } +} diff --git a/src/main/java/com/c2kernel/graph/model/GraphModelCastorData.java b/src/main/java/com/c2kernel/graph/model/GraphModelCastorData.java new file mode 100644 index 0000000..7717c33 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/GraphModelCastorData.java @@ -0,0 +1,29 @@ +package com.c2kernel.graph.model; + + +public class GraphModelCastorData +{ + public String mClassNameOfVertexOutlineCreator = ""; + public Vertex[] mVertexImpls = {}; + public DirectedEdge[] mEdgeImpls = {}; + public int mStartVertexId = 0; + public int mNextId = 0; + + + public GraphModelCastorData() + { + } + + public GraphModelCastorData(String classNameOfVertexOutlineCreator, + Vertex[] vertexImpls, + DirectedEdge[] edgeImpls, + int startVertexId, + int nextId) + { + mClassNameOfVertexOutlineCreator = classNameOfVertexOutlineCreator; + mVertexImpls = vertexImpls; + mEdgeImpls = edgeImpls; + mStartVertexId = startVertexId; + mNextId = nextId; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/GraphModelManager.java b/src/main/java/com/c2kernel/graph/model/GraphModelManager.java new file mode 100644 index 0000000..68d47e0 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/GraphModelManager.java @@ -0,0 +1,143 @@ +package com.c2kernel.graph.model; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.Observable; +import java.util.Stack; + +import com.c2kernel.graph.event.EntireModelChangedEvent; +import com.c2kernel.graph.event.ForcedNotifyEvent; +import com.c2kernel.graph.event.GraphModelEvent; +import com.c2kernel.utils.Logger; + + +public class GraphModelManager extends Observable +{ + + private GraphModel mGraphModel; + private EdgeFactory mEdgeFactory; + private VertexFactory mVertexFactory; + private VertexOutlineCreator mVertexOutlineCreator; + private EntireModelChangedEvent mEntireModelChangedEvent = new EntireModelChangedEvent(); + private ForcedNotifyEvent mForcedNotifyEvent = new ForcedNotifyEvent(); + private Stack mParentModels = new Stack(); + private ArrayList mParentIds = new ArrayList(); + private boolean mEditable = true; + + // Calling this constructor does not create a vertex outline creator + // which is required by the method addVertexAndCreateId() + public GraphModelManager() + { + mGraphModel = new GraphModel(); + mGraphModel.setManager(this); + } + + public GraphModelManager(GraphModel newModel) { + newModel.setManager(this); + mGraphModel = newModel; + } + + public void replace(GraphModel newModel) { + mParentModels.clear(); + + //zoom back to where we were + for (Iterator iter = mParentIds.iterator(); iter.hasNext();) { + Integer parentId = iter.next(); + GraphableVertex childModelVertex = (GraphableVertex)newModel.getVertexById(parentId.intValue()); + if (childModelVertex == null) { // we've been deleted, stay here + Logger.msg(7, "Didn't find "+parentId+" in new model tree. Stopping here."); + do { iter.remove(); } while (iter.hasNext()); + break; + } + else { + mParentModels.push(newModel); + Logger.msg(7, "Pushing model and switching to "+parentId); + newModel = childModelVertex.getChildGraphModel(); + } + } + setModel(newModel); + } + + public void setModel(GraphModel newModel) { + // reset transient data + newModel.mSelection = new Selection(null, null, 0, 0, 0, 0); + newModel.mNewEdgeOriginVertex = null; + newModel.mNewEdgeEndPoint = null; + + // copy factories over + newModel.setExternalEdgeFactory(mEdgeFactory); + newModel.setExternalVertexFactory(mVertexFactory); + newModel.setVertexOutlineCreator(mVertexOutlineCreator); + mVertexFactory.setCreationContext(newModel.getContainingVertex()); + newModel.setManager(this); + mGraphModel.setManager(null); + mGraphModel = newModel; + + // notify + setChanged(); + notifyObservers(mEntireModelChangedEvent); + } + + public void zoomIn(Vertex child) { + GraphModel childModel = child.getChildGraphModel(); + if (childModel != null) { + mParentModels.push(mGraphModel); + mParentIds.add(new Integer(child.getID())); + setModel(childModel); + Logger.msg(7, "ZoomIn - Stack size: "+mParentModels.size()+" ids:"+mParentIds.size()); + } + } + + public void zoomOut() { + if (!mParentModels.empty()) { + setModel(mParentModels.pop()); + mParentIds.remove(mParentIds.size()-1); + } + Logger.msg(7, "ZoomOut - Stack size: "+mParentModels.size()+" ids:"+mParentIds.size()); + + } + + public void forceNotify() + { + setChanged(); + notifyObservers(mForcedNotifyEvent); + } + + public GraphModel getModel() { + return mGraphModel; + } + + public void setExternalEdgeFactory(EdgeFactory newEdgeFactory) { + mEdgeFactory = newEdgeFactory; + mGraphModel.setExternalEdgeFactory(newEdgeFactory); + } + + public void setExternalVertexFactory(VertexFactory newVertexFactory) { + mVertexFactory = newVertexFactory; + mGraphModel.setExternalVertexFactory(newVertexFactory); + } + + public void setVertexOutlineCreator(VertexOutlineCreator newVertexOutlineCreator) { + mVertexOutlineCreator = newVertexOutlineCreator; + mGraphModel.setVertexOutlineCreator(newVertexOutlineCreator); + } + + @Override + protected void setChanged() { + super.setChanged(); + } + + protected void notifyObservers(GraphModelEvent ev) { + super.notifyObservers(ev); + } + + public void setEditable(boolean editable) { + mEditable = editable; + } + + public boolean isEditable() { + return mEditable; + } + + +} diff --git a/src/main/java/com/c2kernel/graph/model/GraphPoint.java b/src/main/java/com/c2kernel/graph/model/GraphPoint.java new file mode 100644 index 0000000..f2aa165 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/GraphPoint.java @@ -0,0 +1,18 @@ +package com.c2kernel.graph.model; + +import java.io.Serializable; + +public class GraphPoint implements Serializable{ + + public int x; + public int y; + + public GraphPoint() { + x=0; y=0; + } + + public GraphPoint(int x, int y) { + this.x = x; + this.y = y; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/Graphable.java b/src/main/java/com/c2kernel/graph/model/Graphable.java new file mode 100644 index 0000000..ed55271 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/Graphable.java @@ -0,0 +1,55 @@ +package com.c2kernel.graph.model; + +/** +* @version $Revision: 1.6 $ $Date: 2003/05/12 13:10:20 $ +* @author $Author: abranson $ +*/ + +import com.c2kernel.utils.CastorHashMap; + +abstract public class Graphable extends Vertex +{ + + protected CastorHashMap mProperties = null; + public GraphModel children; + + public void setProperties(CastorHashMap props) + { + mProperties = props; + } + + public CastorHashMap getProperties() + { + return mProperties; + } + /** @associates Graphable that is directly containing it*/ + private Graphable parent; + + /** + * Returns the parent. + * @return Graphable + */ + public Graphable getParent() + { + return parent; + } + + /** + * Sets the parent. + * @param parent The parent to set + */ + public void setParent(Graphable parent) + { + this.parent = parent; + } + @Override + public GraphModel getChildGraphModel() { + return children; + } + + @Override + public Object getCreationContext() { + return this; + } + +} diff --git a/src/main/java/com/c2kernel/graph/model/GraphableEdge.java b/src/main/java/com/c2kernel/graph/model/GraphableEdge.java new file mode 100644 index 0000000..23499be --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/GraphableEdge.java @@ -0,0 +1,71 @@ +package com.c2kernel.graph.model; + +import com.c2kernel.utils.CastorHashMap; +import com.c2kernel.utils.KeyValuePair; + +/** +* @version $Revision: 1.2 $ $Date: 2003/05/12 13:10:20 $ +* @author $Author: abranson $ +*/ +public abstract class GraphableEdge extends DirectedEdge +{ + + private GraphableVertex mParent; + private CastorHashMap mProperties = null; + + public GraphableEdge() + { + mProperties = new CastorHashMap(); + } + + public GraphableEdge(GraphableVertex pre, GraphableVertex nex) + { + mProperties = new CastorHashMap(); + setParent(pre.getParent()); + pre.getParent().getChildrenGraphModel().addEdgeAndCreateId(this, pre, nex); + } + + /** + * Returns the parent. + * @return GraphableVertex + */ + public GraphableVertex getParent() + { + return mParent; + } + + /** + * Sets the parent. + * @param parent The parent to set + */ + public void setParent(GraphableVertex parent) + { + mParent = parent; + } + + /** + * Returns the properties. + * @return CastorHashMap + */ + public CastorHashMap getProperties() + { + return mProperties; + } + + /** + * Sets the properties. + * @param properties The properties to set + */ + public void setProperties(CastorHashMap properties) + { + mProperties = properties; + } + + public KeyValuePair[] getKeyValuePairs() { + return mProperties.getKeyValuePairs(); + } + + public void setKeyValuePairs(KeyValuePair[] pairs) { + mProperties.setKeyValuePairs(pairs); + } +} diff --git a/src/main/java/com/c2kernel/graph/model/GraphableVertex.java b/src/main/java/com/c2kernel/graph/model/GraphableVertex.java new file mode 100644 index 0000000..fc04743 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/GraphableVertex.java @@ -0,0 +1,261 @@ +package com.c2kernel.graph.model; +/** +* @version $Revision: 1.24 $ $Date: 2005/10/05 07:39:37 $ +* @author $Author: abranson $ +*/ +import java.awt.Point; + +import com.c2kernel.utils.CastorHashMap; +import com.c2kernel.utils.KeyValuePair; +public abstract class GraphableVertex extends Vertex +{ + private CastorHashMap mProperties = null; + private boolean mIsLayoutable; + protected boolean mIsComposite; + private GraphModel mChildrenGraphModel; + public GraphableVertex() + { + mProperties = new CastorHashMap(); + } + public void setProperties(CastorHashMap props) + { + mProperties = props; + } + public CastorHashMap getProperties() + { + return mProperties; + } + public KeyValuePair[] getKeyValuePairs() + { + return mProperties.getKeyValuePairs(); + } + public void setKeyValuePairs(KeyValuePair[] pairs) + { + mProperties.setKeyValuePairs(pairs); + } + /** @associates Graphable that is directly containing it*/ + private GraphableVertex parent; + /** + * Returns the parent. + * @return Graphable + */ + public GraphableVertex getParent() + { + return parent; + } + /** + * Sets the parent. + * @param parent The parent to set + */ + public void setParent(GraphableVertex parent) + { + if (this.equals(parent)) + throw new ExceptionInInitializerError(); + this.parent = parent; + } + @Override + public GraphModel getChildGraphModel() + { + return mChildrenGraphModel; + } + @Override + public Object getCreationContext() + { + return this; + } + public Vertex[] getOutGraphables() + { + if (parent == null) + return new Vertex[0]; // none if no parent + return parent.mChildrenGraphModel.getOutVertices(this); + } + public DirectedEdge[] getOutEdges() + { + if (parent == null) + return new DirectedEdge[0]; // none if no parent + return parent.mChildrenGraphModel.getOutEdges(this); + } + public DirectedEdge[] getInEdges() + { + if (parent == null) + return new DirectedEdge[0]; // none if no parent + DirectedEdge[] edges = getParent().mChildrenGraphModel.getInEdges(this); + if (edges != null) + return edges; + else + return new DirectedEdge[0]; + } + public GraphableVertex[] getChildren() + { + return getLayoutableChildren(); + } + + public DirectedEdge[] getChildrenEdges() + { + if (getIsComposite()) + { + return getChildGraphModel().getEdges(); + } + return null; + } + + public GraphableVertex[] getLayoutableChildren() + { + if (getIsComposite()) + { + Vertex[] vs = mChildrenGraphModel.getVertices(); + GraphableVertex[] gvs = new GraphableVertex[vs.length]; + for (int i = 0; i < vs.length; i++) + { + gvs[i] = (GraphableVertex) vs[i]; + } + return gvs; + } + return null; + } + // deprecated methods + public GraphableVertex[] getCNonLayoutableChildren() { + return new GraphableVertex[0]; + } + public void setCNonLayoutableChildren(GraphableVertex[] dummy) { } + + /**@returns the Graphable searched or null if not this or children*/ + public GraphableVertex search(String ids) + { + if (getName().equals(ids)) + return this; + if (String.valueOf(getID()).equals(ids)) + return this; + if (getIsComposite()) + { + GraphableVertex[] graphables = getChildren(); + if (ids.startsWith(String.valueOf(getID()))) + ids = ids.substring(ids.indexOf("/") + 1); + else if (ids.startsWith(getName())) + ids = ids.substring(getName().length() + 1); + else if (ids.startsWith(getPath())) + ids = ids.substring(getPath().length() + 1); + else + return null; + + for (GraphableVertex graphable : graphables) { + GraphableVertex grap = graphable.search(ids); + if (grap != null) return grap; + } + } + return null; + } + /** + * Returns the isLayoutable. + * @return boolean + */ + public boolean getIsLayoutable() + { + return mIsLayoutable; + } + /** + * Sets the isLayoutable. + * @param isLayoutable The isLayoutable to set + */ + public void setIsLayoutable(boolean isLayoutable) + { + mIsLayoutable = isLayoutable; + } + /** + * Returns the isComposite. + * @return boolean + */ + public boolean getIsComposite() + { + return mIsComposite; + } + /** + * Sets the isComposite. + * @param isComposite The isComposite to set + */ + public void setIsComposite(boolean isComposite) + { + mIsComposite = isComposite; + } + public void addChild(GraphableVertex graphableVertex, Point p) + { + addChild(graphableVertex, new GraphPoint(p.x, p.y)); + } + public void addChild(GraphableVertex graphableVertex, GraphPoint g) + { + getChildGraphModel().addVertexAndCreateId(graphableVertex, g); + graphableVertex.setParent(this); + } + /** + * Returns the childrenGraph. + * @return GraphModel + */ + public GraphModel getChildrenGraphModel() + { + return mChildrenGraphModel; + } + /** + * Sets the childrenGraph. + * @param childrenGraph The childrenGraph to set + */ + public void setChildrenGraphModel(GraphModel childrenGraph) + { + mChildrenGraphModel = childrenGraph; + DirectedEdge[] edges = mChildrenGraphModel.getEdges(); + GraphableVertex[] graphables = this.getLayoutableChildren(); + if (graphables != null) + for (GraphableVertex graphable : graphables) + graphable.setParent(this); + if (edges != null) + for (DirectedEdge edge : edges) + ((GraphableEdge) edge).setParent(this); + childrenGraph.setContainingVertex(this); + } + + /** + * @see com.c2kernel.graph.model.Vertex#getCentrePoint() + */ + @Override + public GraphPoint getCentrePoint() + { + if (!getIsLayoutable()) + return null; + return super.getCentrePoint(); + } + /** + * @see com.c2kernel.graph.model.Vertex#getInEdgeIds() + */ + @Override + public int[] getInEdgeIds() + { + if (!getIsLayoutable()) + return null; + return super.getInEdgeIds(); + } + /** + * @see com.c2kernel.graph.model.Vertex#getOutEdgeIds() + */ + @Override + public int[] getOutEdgeIds() + { + if (!getIsLayoutable()) + return null; + return super.getOutEdgeIds(); + } + /** + * @see com.c2kernel.graph.model.Vertex#getOutlinePoints() + */ + @Override + public GraphPoint[] getOutlinePoints() + { + if (!getIsLayoutable()) + return null; + return super.getOutlinePoints(); + } + public String getPath() + { + if (getName() != null && !getName().equals("")) + return getParent().getPath() + "/" + getName(); + return getParent().getPath() + "/" + getID(); + } +} \ No newline at end of file diff --git a/src/main/java/com/c2kernel/graph/model/Selection.java b/src/main/java/com/c2kernel/graph/model/Selection.java new file mode 100644 index 0000000..dcc7b46 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/Selection.java @@ -0,0 +1,35 @@ +package com.c2kernel.graph.model; + +import java.io.Serializable; + + + +public class Selection implements Serializable +{ + // Either a single edge can be selected or + // one or more vertices can be selected. + // It is impossible to select an edge and a + // vertex at the same time. + public DirectedEdge mEdge = null; + public Vertex[] mVertices = null; + public int mTopLeftX = 0; + public int mTopLeftY = 0; + public int mBottomRightX = 0; + public int mBottomRightY = 0; + + + public Selection(DirectedEdge edge, + Vertex[] vertices, + int topLeftX, + int topLeftY, + int bottomRightX, + int bottomRightY) + { + mEdge = edge; + mVertices = vertices; + mTopLeftX = topLeftX; + mTopLeftY = topLeftY; + mBottomRightX = bottomRightX; + mBottomRightY = bottomRightY; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/TypeNameAndConstructionInfo.java b/src/main/java/com/c2kernel/graph/model/TypeNameAndConstructionInfo.java new file mode 100644 index 0000000..e5b6c3d --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/TypeNameAndConstructionInfo.java @@ -0,0 +1,24 @@ +package com.c2kernel.graph.model; + +import java.io.Serializable; + + +public class TypeNameAndConstructionInfo implements Serializable +{ + public String mName = null; + public Object mInfo = null; + + + public TypeNameAndConstructionInfo(String name, Object info) + { + mName = name; + mInfo = info; + } + + + @Override + public String toString() + { + return mName; + } +} diff --git a/src/main/java/com/c2kernel/graph/model/Vertex.java b/src/main/java/com/c2kernel/graph/model/Vertex.java new file mode 100644 index 0000000..ccef437 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/model/Vertex.java @@ -0,0 +1,308 @@ +package com.c2kernel.graph.model; + +import java.awt.Polygon; +import java.io.Serializable; +import java.util.Vector; + + +public class Vertex implements Serializable +{ + private int mId = -1; + private String mName = ""; + private GraphPoint mCentrePoint = new GraphPoint(0, 0); + private int mHeight = 0; + private int mWidth = 0; + private Vector mInEdgeIdVector = new Vector(); + private Vector mOutEdgeIdVector = new Vector(); + private Vector mTags = new Vector(); + + // The Java Polygon class is used to determine if a point + // lies within the outline of a vertex. Unfortunately + // both the polygon and the set of outline points need to + // kept in memory because a polygon never has 0 points + // which is required by Castor's unmarshall object mechanism + private Polygon mOutlinePolygon = new Polygon(); + private GraphPoint[] mOutlinePoints = new GraphPoint[0]; + + + private GraphModel graphModel; + + public void setID(int id) + { + mId = id; + } + + + public int getID() + { + return mId; + } + + + public void setName(String n) + { + mName = n; + } + + + public String getName() + { + return mName; + } + + + public void setCentrePoint(GraphPoint p) + { + mCentrePoint = p; + } + + + public GraphPoint getCentrePoint() + { + return mCentrePoint; + } + + + public void setHeight(int h) + { + mHeight = h; + } + + + public int getHeight() + { + return mHeight; + } + + + public void setWidth(int w) + { + mWidth = w; + } + + + public int getWidth() + { + return mWidth; + } + + + // Sets the outline points and re-calculates the + // height and width + public void setOutlinePoints(GraphPoint[] outline) + { + int topLeftX = outline[0].x; + int topLeftY = outline[0].y; + int bottomRightX = 0; + int bottomRightY = 0; + int i = 0; + + mOutlinePoints = outline; + + // Construct a polygon in the outline of the vertex + // and calculate the top left and bottom right corners + mOutlinePolygon = new Polygon(); + + for(i=0; i bottomRightX) + { + bottomRightX = outline[i].x; + } + + + if(outline[i].y > bottomRightY) + { + bottomRightY = outline[i].y; + } + } + + // Set the height and width + mHeight = bottomRightY - topLeftY; + mWidth = bottomRightX - topLeftX; + } + + + public GraphPoint[] getOutlinePoints() + { + return mOutlinePoints; + } + + public void moveAbsolute(GraphPoint p) + { + int deltaX = p.x - mCentrePoint.x; + int deltaY = p.y - mCentrePoint.y; + int i = 0; + + // Update the outline points and the polygon + for(i=0; i(10, 10); + for(i=0; i(10, 10); + for(i=0; i vector) + { + int[] array = new int[vector.size()]; + Integer integer = null; + int i = 0; + + for(i=0; i path = new Vector(10, 10); + + graphModel.clearTags(startVertex); + visitVertex(startVertex, graphModel, path, direction, startVertex, ignoreBackLinks); + + return vectorToVertexArray(path); + } + + + private static void visitVertex(Vertex vertex, GraphModel graphModel, Vector path, int direction, Object tag, boolean ignoreBackLinks) + { + Vertex[] children = null; + int i = 0; + + if(direction == kDown) + { + children = graphModel.getOutVertices(vertex); + } + else + { + children = graphModel.getInVertices(vertex); + } + + vertex.setTag(tag); + path.add(vertex); + + for(i=0; i vector) + { + Vertex[] vertices = new Vertex[vector.size()]; + int i = 0; + + + for(i=0; i 0)) return Math.PI; + + if((width == 0) && (height < 0)) return 0; + + if((width > 0) && (height == 0)) return Math.PI/2.0; + + if((width < 0) && (height == 0)) return -1.0 * Math.PI/2.0; + + if((width > 0) && (height > 0)) return Math.PI/2.0 + Math.atan(Math.abs(height)/Math.abs(width)); + + if((width > 0) && (height < 0)) return Math.atan(Math.abs(width)/Math.abs(height)); + + if((width < 0) && (height < 0)) return -1.0 * Math.atan(Math.abs(width)/Math.abs(height)); + + if((width < 0) && (height > 0)) return -1.0 * (Math.PI/2.0 + Math.atan(Math.abs(height)/Math.abs(width))); + + return 0.0; + } +} diff --git a/src/main/java/com/c2kernel/graph/view/DefaultVertexRenderer.java b/src/main/java/com/c2kernel/graph/view/DefaultVertexRenderer.java new file mode 100644 index 0000000..e9b92d0 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/DefaultVertexRenderer.java @@ -0,0 +1,60 @@ +package com.c2kernel.graph.view; + +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.Polygon; + +import com.c2kernel.graph.model.GraphPoint; +import com.c2kernel.graph.model.Vertex; + + +public class DefaultVertexRenderer implements VertexRenderer +{ + private Paint mLinePaint = null; + private Paint mTextPaint = null; + private Paint mFillPaint = null; + + + public DefaultVertexRenderer(Paint linePaint, Paint textPaint, Paint fillPaint) + { + mLinePaint = linePaint; + mTextPaint = textPaint; + mFillPaint = fillPaint; + } + + + @Override + public void draw(Graphics2D g2d, Vertex vertex) + { + GraphPoint[] outlinePoints = vertex.getOutlinePoints(); + GraphPoint centrePoint = vertex.getCentrePoint(); + Polygon outline = new Polygon(); + + String vertexName = vertex.getName(); + FontMetrics metrics = g2d.getFontMetrics(); + int textWidth = metrics.stringWidth(vertexName); + int textHeight = metrics.getHeight(); + int textX = centrePoint.x - textWidth/2; + int textY = centrePoint.y + textHeight/3; + + int i = 0; + + + // Construct a shape in the outline of the vertex + for(i=0; i mListenerVector = new Vector(10, 10); + public EditorToolBar(boolean edgeCreationMode, // True if edges can be created + JButton[] otherButtons, GraphPanel graphP) + { + super(BoxLayout.X_AXIS); + mGraphPanel = graphP; + mEdgeCreationMode = edgeCreationMode; + mOtherButtons = otherButtons; + prepareModeButtons(); + mStartVertexController.setStartButton(mStartButton); + mDeletionController.setDeleteButton(mDeleteButton); + createLayout(); + createListeners(); + } + protected void prepareModeButtons() + { + // Set the tool tip texts + mVertexModeButton.setToolTipText(Language.translate("Create vertex")); + mSelectModeButton.setToolTipText(Language.translate("Multi-select and drag")); + mEdgeModeButton.setToolTipText(Language.translate("Create edge")); + mStartButton.setToolTipText(Language.translate("Select the start vertex of the graph")); + mDeleteButton.setToolTipText(Language.translate("Delete the selection")); + mPrintButton.setToolTipText(Language.translate("Print this graph")); + mPrintButton.setToolTipText(Language.translate("Copy an image of this graph to the clipboard")); + // Set the button margins to 0 + mVertexModeButton.setMargin(new Insets(0, 0, 0, 0)); + mSelectModeButton.setMargin(new Insets(0, 0, 0, 0)); + mEdgeModeButton.setMargin(new Insets(0, 0, 0, 0)); + // The initial mode is select mode + mSelectModeButton.setSelected(true); + // Add the mode buttons to the mode button group + mModeButtonGroup.add(mVertexModeButton); + mModeButtonGroup.add(mSelectModeButton); + mModeButtonGroup.add(mEdgeModeButton); + // Add the action listeners + mVertexModeButton.addActionListener(new ModeButtonListener("Vertex")); + mSelectModeButton.addActionListener(new ModeButtonListener("Select")); + mEdgeModeButton.addActionListener(new ModeButtonListener("Edge")); + } + public void enterSelectMode() + { + mSelectModeButton.setSelected(true); + notifyListeners("Select"); + } + public void updateVertexTypes(TypeNameAndConstructionInfo[] typeNameAndConstructionInfo) + { + int i = 0; + mVertexTypeBox.removeAllItems(); + for (i = 0; i < typeNameAndConstructionInfo.length; i++) + { + mVertexTypeBox.addItem(typeNameAndConstructionInfo[i]); + } + } + public void updateEdgeTypes(TypeNameAndConstructionInfo[] typeNameAndConstructionInfo) + { + int i = 0; + mEdgeTypeBox.removeAllItems(); + for (i = 0; i < typeNameAndConstructionInfo.length; i++) + { + mEdgeTypeBox.addItem(typeNameAndConstructionInfo[i]); + } + } + public TypeNameAndConstructionInfo getSelectedVertexType() + { + return (TypeNameAndConstructionInfo) mVertexTypeBox.getSelectedItem(); + } + public TypeNameAndConstructionInfo getSelectedEdgeType() + { + return (TypeNameAndConstructionInfo) mEdgeTypeBox.getSelectedItem(); + } + protected void createLayout() + { + int i = 0; + add(mSelectModeButton); + add(mVertexModeButton); + add(mVertexTypeBox); + add(Box.createHorizontalStrut(10)); + if (mEdgeCreationMode) + { + add(mEdgeModeButton); + add(mEdgeTypeBox); + } + add(Box.createGlue()); + mPrintButton.setEnabled(true); + mPrintButton.setMargin(new Insets(0, 0, 0, 0)); + add(mPrintButton); + mCopyButton.setEnabled(true); + mCopyButton.setMargin(new Insets(0, 0, 0, 0)); + add(mCopyButton); + mStartButton.setEnabled(false); + mStartButton.setMargin(new Insets(0, 0, 0, 0)); + mDeleteButton.setEnabled(false); + mDeleteButton.setMargin(new Insets(0, 0, 0, 0)); + add(mDeleteButton); + add(Box.createRigidArea(new Dimension(20, 0))); + add(mStartButton); + if (mOtherButtons != null) + { + for (i = 0; i < mOtherButtons.length; i++) + { + mOtherButtons[i].setMargin(new Insets(0, 0, 0, 0)); + add(mOtherButtons[i]); + } + } + } + protected void createListeners() + { + // The vertex mode button should be selected if the + // user select a vertex type from the vertex type box + mVertexTypeBox.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent ae) + { + mVertexModeButton.setSelected(true); + notifyListeners("Vertex"); + } + }); + // The edge mode button should be selected if the + // user select an edge type from the edge type box + mEdgeTypeBox.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent ae) + { + mEdgeModeButton.setSelected(true); + notifyListeners("Edge"); + } + }); + mPrintButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent ae) + { + PrinterJob _monJob = PrinterJob.getPrinterJob(); + if (_monJob.printDialog()) + _monJob.setPrintable(self()); + try + { + _monJob.print(); + } + catch (Exception ex) + { + } + } + }); + + try { + Class.forName("java.awt.datatransfer.DataFlavor").getDeclaredField("imageFlavor"); + mCopyButton.addActionListener(new ActionListener() + { + @Override + public void actionPerformed(ActionEvent ae) + { + try + { + Image i = createImage(mGraphPanel.getWidth(),mGraphPanel.getHeight()); + Graphics g = i.getGraphics(); + mGraphPanel.paintComponent(g); + ImageTransferable it = new ImageTransferable(i, mGraphPanel.getWidth(), mGraphPanel.getHeight()); + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(it, it); + } + catch (Exception e) + { + Logger.error(e); + } + } + }); + } catch (Exception ex) { //image clipboard transfer not supported + mCopyButton.setEnabled(false); + } + } + + protected class ImageTransferable implements Transferable, ClipboardOwner { + Image image; + int width,height; + DataFlavor javaImg; + public ImageTransferable(Image image, int width, int height) { + this.image = image; + this.width = width; + this.height = height; + try { + javaImg = new DataFlavor("image/x-java-image; class=java.awt.Image", "AWT Image"); + } catch (Exception ex){ } + } + @Override + public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { + if (!isDataFlavorSupported(flavor) || image == null) { + throw new UnsupportedFlavorException(flavor); + } + return image; + } + + @Override + public boolean isDataFlavorSupported(DataFlavor flavor) { + boolean result = in(flavor, getTransferDataFlavors()); + return result; + } + + @Override + public DataFlavor[] getTransferDataFlavors() { + return new DataFlavor[] { javaImg }; + } + protected boolean in(DataFlavor flavor, DataFlavor[] flavors) { + int f = 0; + while ((f < flavors.length) && !flavor.equals(flavors[f])) { + f++; + } + return f < flavors.length; + } + @Override + public void lostOwnership(Clipboard clipboard, Transferable contents) { + image = null; + } + } + + protected void notifyListeners(String newModeId) + { + int i = 0; + EditorModeListener listener = null; + for (i = 0; i < mListenerVector.size(); i++) + { + listener = mListenerVector.elementAt(i); + listener.editorModeChanged(newModeId); + } + } + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mStartVertexController.setGraphModelManager(graphModelManager); + mDeletionController.setGraphModelManager(graphModelManager); + } + public void setGraphPanel(GraphPanel graphPanel) + { + graphPanel.addKeyListener(mDeletionController); + } + public void addEditorModeListener(EditorModeListener listener) + { + mListenerVector.add(listener); + } + public void removeEditorModeListener(EditorModeListener listener) + { + mListenerVector.remove(listener); + } + public void setGraphEditable(boolean editable) + { + mVertexModeButton.setEnabled(editable); + mEdgeModeButton.setEnabled(editable); + } + public EditorToolBar self() + { + return this; + } + @Override + public int print(Graphics g, PageFormat pf, int i) throws PrinterException + { + if (i >= 1) + { + return Printable.NO_SUCH_PAGE; + } + Graphics2D g2d = (Graphics2D) g; + double scalex = pf.getImageableWidth() / mGraphPanel.getWidth(); + double scaley = pf.getImageableHeight() / mGraphPanel.getHeight(); + double scale = Math.min(Math.min(scalex, scaley), 1); + g2d.translate(pf.getImageableX(), pf.getImageableY()); + g2d.scale(scale, scale); + mGraphPanel.printComponent(g2d); + return Printable.PAGE_EXISTS; + } +} diff --git a/src/main/java/com/c2kernel/graph/view/GraphPanel.java b/src/main/java/com/c2kernel/graph/view/GraphPanel.java new file mode 100644 index 0000000..012fe25 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/GraphPanel.java @@ -0,0 +1,272 @@ +package com.c2kernel.graph.view; +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Image; +import java.awt.Paint; +import java.awt.Point; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.JPanel; + +import com.c2kernel.graph.event.EntireModelChangedEvent; +import com.c2kernel.graph.event.GraphModelResizedEvent; +import com.c2kernel.graph.model.DirectedEdge; +import com.c2kernel.graph.model.ElasticBand; +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.model.GraphPoint; +import com.c2kernel.graph.model.Selection; +import com.c2kernel.graph.model.Vertex; +import com.c2kernel.utils.Resource; +public class GraphPanel extends JPanel implements Observer +{ + protected final Paint mSelectionPaint = Color.black; + protected final Paint mStartPaint = Color.green; + protected final Image mResizePadImg = Resource.findImage("graph/resizepad.gif").getImage(); + protected final BasicStroke mDashed = + new BasicStroke(1.0f, BasicStroke.CAP_BUTT, BasicStroke.JOIN_MITER, 10.0f, new float[] { 5.0f }, 0.0f); + protected GraphModelManager mGraphModelManager = null; + protected VertexRenderer mVertexRenderer = null; + protected DirectedEdgeRenderer mDirectedEdgeRenderer = null; + public GraphPanel(DirectedEdgeRenderer eRenderer, VertexRenderer vRenderer) + { + mVertexRenderer = vRenderer; + mDirectedEdgeRenderer = eRenderer; + // Request the keyboard focus if the mouse + // is pressed on the graph panel + addMouseListener(new MouseAdapter() + { + @Override + public void mousePressed(MouseEvent me) + { + requestFocus(); + } + }); + } + public void setGraphModelManager(GraphModelManager graphModelManager) + { + mGraphModelManager = graphModelManager; + } + @Override + public void update(Observable o, Object arg) + { + if (arg instanceof GraphModelResizedEvent || arg instanceof EntireModelChangedEvent) + { + setPreferredSize(new Dimension(mGraphModelManager.getModel().getWidth(), mGraphModelManager.getModel().getHeight())); + revalidate(); + } + repaint(); + } + @Override + public void paintComponent(Graphics g) + { + Graphics2D g2d = (Graphics2D) g; + DirectedEdge[] edges = null; + Vertex[] vertices = null; + Vertex startVertex = null; + Selection selection = null; + ElasticBand elasticBand = null; + Vertex newEdgeOriginVertex = null; + GraphPoint newEdgeOriginPoint = null; + Point newEdgeEndPoint = null; + GraphPoint vertexCentre = null; + int i = 0; + super.paintComponent(g); + if (mGraphModelManager != null) + { + // Get the edges and vertices from the model + edges = mGraphModelManager.getModel().getEdges(); + vertices = mGraphModelManager.getModel().getVertices(); + //graphable = mGraphModelManager.getModel(). + // Draw the edges + for (i = 0; i < edges.length; i++) + { + mDirectedEdgeRenderer.draw(g2d, edges[i]); + } + // Draw the vertices + for (i = 0; i < vertices.length; i++) + { + mVertexRenderer.draw(g2d, vertices[i]); + } + g2d.setPaint(mStartPaint); + // Highlight the start vertex if there is one + startVertex = mGraphModelManager.getModel().getStartVertex(); + if (startVertex != null) + { + drawVertexHighlight(g2d, startVertex, 1); + } + // Get the present selection + selection = mGraphModelManager.getModel().getSelection(); + g2d.setPaint(mSelectionPaint); + // Draw the outline of the selected + // vertices if there are any + if (selection.mVertices != null) + { + g2d.setStroke(mDashed); + for (i = 0; i < selection.mVertices.length; i++) + { + if (selection.mVertices[i] != mGraphModelManager.getModel().getContainingVertex()) + drawVertexHighlight(g2d, selection.mVertices[i], 5); + } + // Draw the resize pads if there is one and only one vertex selected + if (selection.mVertices.length == 1 && + selection.mVertices[0] != mGraphModelManager.getModel().getContainingVertex()) + { + vertexCentre = selection.mVertices[0].getCentrePoint(); + g2d.drawImage( + mResizePadImg, + vertexCentre.x + selection.mVertices[0].getWidth() / 2, + vertexCentre.y + selection.mVertices[0].getHeight() / 2, + this); + } + } + // Draw the outline of the selected + // edge if there is one + if (selection.mEdge != null) + { + drawEdgeHighlight(g2d, selection.mEdge); + } + // Get the elastic band + elasticBand = mGraphModelManager.getModel().getElasticBand(); + // Draw the elastic band if there + // is one + if (elasticBand != null) + { + g2d.drawLine( + elasticBand.mFixedCorner.x, + elasticBand.mFixedCorner.y, + elasticBand.mMovingCorner.x, + elasticBand.mFixedCorner.y); + g2d.drawLine( + elasticBand.mMovingCorner.x, + elasticBand.mFixedCorner.y, + elasticBand.mMovingCorner.x, + elasticBand.mMovingCorner.y); + g2d.drawLine( + elasticBand.mMovingCorner.x, + elasticBand.mMovingCorner.y, + elasticBand.mFixedCorner.x, + elasticBand.mMovingCorner.y); + g2d.drawLine( + elasticBand.mFixedCorner.x, + elasticBand.mMovingCorner.y, + elasticBand.mFixedCorner.x, + elasticBand.mFixedCorner.y); + } + // Draw the new edge under construction if there is one + newEdgeEndPoint = mGraphModelManager.getModel().getNewEdgeEndPoint(); + newEdgeOriginVertex = mGraphModelManager.getModel().getNewEdgeOriginVertex(); + if ((newEdgeEndPoint != null) && (newEdgeOriginVertex != null)) + { + newEdgeOriginPoint = newEdgeOriginVertex.getCentrePoint(); + g2d.setPaint(Color.black); + g2d.drawLine(newEdgeOriginPoint.x, newEdgeOriginPoint.y, newEdgeEndPoint.x, newEdgeEndPoint.y); + } + } + } + // Draws the highlight of the specified vertex the specified dist from its outline + protected void drawVertexHighlight(Graphics2D g2d, Vertex vertex, int dist) + { + GraphPoint[] outlinePoints = vertex.getOutlinePoints(); + GraphPoint centrePoint = vertex.getCentrePoint(); + int i = 0; + /* + * float dash1[] ={5.0f}; BasicStroke bs = new BasicStroke(5.0f, BasicStroke.CAP_BUTT,BasicStroke.JOIN_ROUND,10.0f, dash1,0.0f); + */ + for (i = 0; i < outlinePoints.length - 1; i++) + { + drawShiftedLine(dist, g2d, centrePoint, outlinePoints[i].x, outlinePoints[i].y, outlinePoints[i + 1].x, outlinePoints[i + 1].y); + } + drawShiftedLine( + dist, + g2d, + centrePoint, + outlinePoints[outlinePoints.length - 1].x, + outlinePoints[outlinePoints.length - 1].y, + outlinePoints[0].x, + outlinePoints[0].y); + } + // Draws the specifed line the specified distance away from the specified centre point + private static void drawShiftedLine(int dist, Graphics2D g2d, GraphPoint centrePoint, int x1, int y1, int x2, int y2) + { + if (x1 > centrePoint.x) + x1 += dist; + if (x1 < centrePoint.x) + x1 -= dist; + if (y1 > centrePoint.y) + y1 += dist; + if (y1 < centrePoint.y) + y1 -= dist; + if (x2 > centrePoint.x) + x2 += dist; + if (x2 < centrePoint.x) + x2 -= dist; + if (y2 > centrePoint.y) + y2 += dist; + if (y2 < centrePoint.y) + y2 -= dist; + g2d.drawLine(x1, y1, x2, y2); + } + // Draws the highlight of the specified edge + protected void drawEdgeHighlight(Graphics2D g2d, DirectedEdge edge) + { + GraphPoint originPoint = edge.getOriginPoint(); + GraphPoint terminusPoint = edge.getTerminusPoint(); + int midX = originPoint.x + (terminusPoint.x - originPoint.x) / 2; + int midY = originPoint.y + (terminusPoint.y - originPoint.y) / 2; + int minX = midX - 10; + int minY = midY - 10; + int maxX = midX + 10; + int maxY = midY + 10; + g2d.drawLine(minX, minY, maxX, minY); + g2d.drawLine(maxX, minY, maxX, maxY); + g2d.drawLine(maxX, maxY, minX, maxY); + g2d.drawLine(minX, maxY, minX, minY); + } + @Override + public void printComponent(Graphics g) + { + super.paintComponent(g); + Graphics2D g2d = (Graphics2D) g; + DirectedEdge[] edges = null; + Vertex[] vertices = null; + Vertex startVertex = null; + int i = 0; + g.setColor(Color.white); + g2d.fillRect(0,0,getWidth(),getHeight()); + if (mGraphModelManager != null) + { + // Get the edges and vertices from the model + edges = mGraphModelManager.getModel().getEdges(); + vertices = mGraphModelManager.getModel().getVertices(); + //graphable = mGraphModelManager.getModel(). + // Draw the edges + for (i = 0; i < edges.length; i++) + { + mDirectedEdgeRenderer.draw(g2d, edges[i]); + } + // Draw the vertices + for (i = 0; i < vertices.length; i++) + { + mVertexRenderer.draw(g2d, vertices[i]); + } + g2d.setPaint(mStartPaint); + // Highlight the start vertex if there is one + startVertex = mGraphModelManager.getModel().getStartVertex(); + if (startVertex != null) + { + drawVertexHighlight(g2d, startVertex, 1); + } + } + } + + protected void superPaint(Graphics g) + { + super.paintComponent(g); + } +} diff --git a/src/main/java/com/c2kernel/graph/view/PropertyTable.java b/src/main/java/com/c2kernel/graph/view/PropertyTable.java new file mode 100644 index 0000000..c1257aa --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/PropertyTable.java @@ -0,0 +1,40 @@ +package com.c2kernel.graph.view; + +import javax.swing.JTable; +import javax.swing.table.TableCellEditor; +import javax.swing.table.TableCellRenderer; + + +public class PropertyTable extends JTable +{ + public PropertyTable(PropertyTableModel tableModel) + { + super(tableModel); + } + + + @Override + public TableCellRenderer getCellRenderer(int row, int column) { + + + return getDefaultRenderer(getCellClass(row, column)); + + } + + @Override +public TableCellEditor getCellEditor(int row, int column) { + + return getDefaultEditor(getCellClass(row, column)); + + } + + private Class getCellClass(int row, int column) { + Class cellClass = String.class; + + try { + cellClass = dataModel.getValueAt(row, column).getClass(); + } catch (NullPointerException ex) { } + + return cellClass; + } +} diff --git a/src/main/java/com/c2kernel/graph/view/PropertyTableModel.java b/src/main/java/com/c2kernel/graph/view/PropertyTableModel.java new file mode 100644 index 0000000..b1e69b1 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/PropertyTableModel.java @@ -0,0 +1,135 @@ +package com.c2kernel.graph.view; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashMap; + +import javax.swing.JOptionPane; +import javax.swing.event.TableModelEvent; +import javax.swing.table.AbstractTableModel; + +import com.c2kernel.utils.Language; + +/************************************************************************** + * + * $Revision: 1.4 $ + * $Date: 2005/08/02 07:50:10 $ + * + * Copyright (C) 2003 CERN - European Organization for Nuclear Research + * All rights reserved. + **************************************************************************/ + +public class PropertyTableModel extends AbstractTableModel { + + private String[] mColumnNames = { Language.translate("Name"), Language.translate("Value") }; + HashMap sourceMap = new HashMap(); + ArrayList sortedNameList = new ArrayList(); + boolean isEditable = false; + + public PropertyTableModel() { + super(); + } + + @Override + public int getColumnCount() + { + return mColumnNames.length; + } + @Override + public String getColumnName(int col) + { + return mColumnNames[col]; + } + @Override + public int getRowCount() + { + synchronized (sourceMap) { + return sourceMap.size(); + } + } + @Override + public Object getValueAt(int rowIndex, int colIndex) + { + synchronized (sourceMap) { + String rowName = sortedNameList.get(rowIndex); + if (colIndex == 0) + return rowName; + else + return sourceMap.get(rowName); + } + } + + @Override + public void setValueAt(Object value, int rowIndex, int colIndex) + { + synchronized (sourceMap) { + if (colIndex == 0) return; + String rowName = sortedNameList.get(rowIndex); + Class oldElement = sourceMap.get(rowName).getClass(); + if (oldElement == Float.class && value.getClass() == String.class) + try { + value = Float.valueOf((String)value); + } catch (Exception ex) { } + if (value.getClass() != oldElement) + JOptionPane.showMessageDialog(null, "This property should contain a "+oldElement.getName()+" not a "+value.getClass().getName(), "Incorrect datatype", JOptionPane.ERROR_MESSAGE); + else { + sourceMap.put(rowName, value); + fireTableCellUpdated(rowIndex, colIndex); + } + } + } + + public void setMap(HashMap props) { + synchronized (sourceMap) { + sourceMap = props; + sortedNameList = new ArrayList(props.size()); + for (String string : props.keySet()) + sortedNameList.add(string); + + Collections.sort(sortedNameList, new Comparator() { + @Override + public int compare(String o1, String o2) { + return (o1.compareToIgnoreCase(o2)); + } + }); + } + fireTableChanged(new TableModelEvent(this)); + } + + @Override + public boolean isCellEditable(int row, int col) + { + return col==1 && isEditable; + } + + /** + * @return Returns the isEditable. + */ + public boolean isEditable() { + return isEditable; + } + /** + * @param isEditable The isEditable to set. + */ + public void setEditable(boolean isEditable) { + this.isEditable = isEditable; + } + + /** + * @param text + * @param object + */ + public void addProperty(String text, Object object) { + sourceMap.put(text,object); + setMap(sourceMap); + } + + /** + * @param object + */ + public void delProperty(Object propName) { + sourceMap.remove(propName); + setMap(sourceMap); + } +} diff --git a/src/main/java/com/c2kernel/graph/view/SelectedVertexPanel.java b/src/main/java/com/c2kernel/graph/view/SelectedVertexPanel.java new file mode 100644 index 0000000..966f527 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/SelectedVertexPanel.java @@ -0,0 +1,27 @@ +package com.c2kernel.graph.view; + +import javax.swing.JPanel; + +import com.c2kernel.graph.model.Vertex; + +/************************************************************************** + * + * $Revision: 1.1 $ + * $Date: 2005/05/12 10:12:52 $ + * + * Copyright (C) 2003 CERN - European Organization for Nuclear Research + * All rights reserved. + **************************************************************************/ + + + +public abstract class SelectedVertexPanel extends JPanel { + + public SelectedVertexPanel() { + super(); + } + + public abstract void select(Vertex vert); + + public abstract void clear(); +} diff --git a/src/main/java/com/c2kernel/graph/view/VertexPropertyPanel.java b/src/main/java/com/c2kernel/graph/view/VertexPropertyPanel.java new file mode 100644 index 0000000..4e3e711 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/VertexPropertyPanel.java @@ -0,0 +1,259 @@ +package com.c2kernel.graph.view; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.lang.reflect.Constructor; +import java.util.HashMap; +import java.util.Observable; +import java.util.Observer; + +import javax.swing.Box; +import javax.swing.JButton; +import javax.swing.JComboBox; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.event.TableModelEvent; +import javax.swing.event.TableModelListener; + +import com.c2kernel.graph.event.EntireModelChangedEvent; +import com.c2kernel.graph.event.SelectionChangedEvent; +import com.c2kernel.graph.model.DirectedEdge; +import com.c2kernel.graph.model.GraphModelManager; +import com.c2kernel.graph.model.GraphableEdge; +import com.c2kernel.graph.model.GraphableVertex; +import com.c2kernel.graph.model.Vertex; +import com.c2kernel.gui.tabs.EntityTabPane; +import com.c2kernel.utils.Language; +import com.c2kernel.utils.Logger; + +/************************************************************************** + * + * $Revision: 1.4 $ + * $Date: 2005/09/09 12:19:28 $ + * + * Copyright (C) 2003 CERN - European Organization for Nuclear Research + * All rights reserved. + **************************************************************************/ + +public class VertexPropertyPanel extends JPanel implements Observer, TableModelListener, ActionListener { + + private final PropertyTableModel mPropertyModel; + private final PropertyTable mPropertyTable; + private GraphModelManager mGraphModelManager; + private boolean isEditable = false; + GridBagLayout gridbag = new GridBagLayout(); + protected JLabel selObjName; + protected JLabel selObjClass; + JButton addPropButton; + JButton delPropButton; + Box newPropBox; + private JTextField newPropName; + private JComboBox newPropType; + String[] typeOptions = { "String", "Boolean", "Integer", "Float" }; + String[] typeInitVal = { "", "false", "0", "0.0"}; + SelectedVertexPanel mSelPanel; + + public VertexPropertyPanel() { + super(); + setLayout(gridbag); + mPropertyModel = new PropertyTableModel(); + mPropertyModel.addTableModelListener(this); + mPropertyTable = new PropertyTable(mPropertyModel); + } + + /** + * + */ + + @Override + public void update(Observable o, Object arg) { + Vertex[] selectedVertices = null; + DirectedEdge selectedEdge = null; + // If the selection has changed + if (arg instanceof SelectionChangedEvent) + { + SelectionChangedEvent event = (SelectionChangedEvent) arg; + selectedVertices = event.mSelection.mVertices; + if (selectedVertices != null) + { + if (selectedVertices.length == 1) + { + setVertex(selectedVertices[0]); + return; + } + } + selectedEdge = event.mSelection.mEdge; + if (selectedEdge != null) + { + setEdge(selectedEdge); + return; + } + } + if (arg instanceof SelectionChangedEvent || arg instanceof EntireModelChangedEvent){ + clear(); + } + } + + + @Override + public void tableChanged(TableModelEvent e) { + if (mGraphModelManager!=null) + mGraphModelManager.forceNotify(); + + } + + public void setVertex(Vertex vert) { + if (vert.getName().equals("domain")) + selObjName.setText("Domain Workflow"); + else + selObjName.setText(vert.getName()); + String className = vert.getClass().getName(); + selObjClass.setText(className.substring(className.lastIndexOf('.')+1)); + if (mSelPanel != null) mSelPanel.select(vert); + if (vert instanceof GraphableVertex) { + mPropertyModel.setMap(((GraphableVertex)vert).getProperties()); + addPropButton.setEnabled(isEditable); + delPropButton.setEnabled(isEditable); + } + } + + public void setEdge(DirectedEdge edge) { + selObjName.setText(edge.getName()); + String className = edge.getClass().getName(); + selObjClass.setText(className.substring(className.lastIndexOf('.')+1)); + if (edge instanceof GraphableEdge) { + mPropertyModel.setMap(((GraphableEdge)edge).getProperties()); + addPropButton.setEnabled(isEditable); + delPropButton.setEnabled(isEditable); + } + if (mSelPanel != null) mSelPanel.clear(); + } + + public void clear() { + selObjName.setText(""); + selObjClass.setText("Nothing Selected"); + mPropertyModel.setMap(new HashMap()); + if (mSelPanel != null) mSelPanel.clear(); + addPropButton.setEnabled(false); + delPropButton.setEnabled(false); + } + + /** + * @param isEditable The isEditable to set. + */ + public void setEditable(boolean editable) { + mPropertyModel.setEditable(editable); + isEditable = editable; + newPropBox.setVisible(editable); + } + + public void setGraphModelManager(GraphModelManager manager) { + mGraphModelManager = manager; + manager.addObserver(this); + } + + public void createLayout(SelectedVertexPanel selPanel) + { + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.weightx = 1; + c.weighty = 0; + c.anchor = GridBagConstraints.NORTHWEST; + c.ipadx = 5; + c.ipady = 5; + + selObjName = new JLabel(); + selObjName.setFont(EntityTabPane.titleFont); + gridbag.setConstraints(selObjName, c); + add(selObjName); + + c.gridy++; + selObjClass = new JLabel(); + gridbag.setConstraints(selObjClass, c); + add(selObjClass); + + c.gridy++; + JLabel title = new JLabel("Properties"); + title.setFont(EntityTabPane.titleFont); + gridbag.setConstraints(title, c); + add(title); + + c.gridy++; + c.fill = GridBagConstraints.BOTH; + c.weighty = 2; + JScrollPane scroll = new JScrollPane(mPropertyTable); + gridbag.setConstraints(scroll, c); + add(scroll); + + newPropBox = Box.createHorizontalBox(); + newPropBox.add(new JLabel(Language.translate("New :"))); + newPropBox.add(Box.createHorizontalGlue()); + newPropName = new JTextField(15); + newPropBox.add(newPropName); + newPropType = new JComboBox(typeOptions); + newPropBox.add(newPropType); + newPropBox.add(Box.createHorizontalStrut(1)); + addPropButton = new JButton("Add"); + addPropButton.setMargin(new Insets(0, 0, 0, 0)); + delPropButton = new JButton("Del"); + delPropButton.setMargin(new Insets(0, 0, 0, 0)); + addPropButton.addActionListener(this); + delPropButton.addActionListener(this); + newPropBox.add(addPropButton); + newPropBox.add(delPropButton); + + c.gridy++; + c.weighty=0; + c.fill= GridBagConstraints.HORIZONTAL; + gridbag.setConstraints(newPropBox, c); + add(newPropBox); + + if (selPanel != null) { + c.gridy++; + mSelPanel = selPanel; + gridbag.setConstraints(mSelPanel, c); + add(mSelPanel); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource() == addPropButton) { + if (newPropName.getText().length() < 1) { + JOptionPane.showMessageDialog(this, "Enter a name for the new property", "Cannot add property", JOptionPane.ERROR_MESSAGE); + return; + } + if (mPropertyModel.sourceMap.containsKey(newPropName.getText())) { + JOptionPane.showMessageDialog(this, "Property '"+newPropName.getText()+"' already exists.", "Cannot add property", JOptionPane.ERROR_MESSAGE); + return; + } + if (mPropertyTable.getCellEditor() != null) + mPropertyTable.getCellEditor().stopCellEditing(); + + try { + Class newPropClass = Class.forName("java.lang."+typeOptions[newPropType.getSelectedIndex()]); + Class[] params = {String.class}; + Constructor init = newPropClass.getConstructor(params); + Object[] initParams = { typeInitVal[newPropType.getSelectedIndex()] }; + mPropertyModel.addProperty(newPropName.getText(), init.newInstance(initParams)); + } catch (Exception ex) { + Logger.exceptionDialog(ex); + } + } + else if (e.getSource() == delPropButton) { + int selrow = mPropertyTable.getSelectedRow(); + if (selrow == -1) { + JOptionPane.showMessageDialog(this, "Select a property to remove", "Cannot delete property", JOptionPane.ERROR_MESSAGE); + return; + } + mPropertyModel.delProperty(mPropertyModel.sortedNameList.get(selrow)); + } + } +} diff --git a/src/main/java/com/c2kernel/graph/view/VertexRenderer.java b/src/main/java/com/c2kernel/graph/view/VertexRenderer.java new file mode 100644 index 0000000..1a8d263 --- /dev/null +++ b/src/main/java/com/c2kernel/graph/view/VertexRenderer.java @@ -0,0 +1,11 @@ +package com.c2kernel.graph.view; + +import java.awt.Graphics2D; + +import com.c2kernel.graph.model.Vertex; + + +public interface VertexRenderer +{ + public void draw(Graphics2D g2d, Vertex vertex); +} -- cgit v1.2.3