summaryrefslogtreecommitdiff
path: root/src/main/java/com/c2kernel/gui/graph
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/com/c2kernel/gui/graph')
-rw-r--r--src/main/java/com/c2kernel/gui/graph/controller/AutoScrollController.java41
-rw-r--r--src/main/java/com/c2kernel/gui/graph/controller/DeletionController.java92
-rw-r--r--src/main/java/com/c2kernel/gui/graph/controller/EdgeConstructionController.java253
-rw-r--r--src/main/java/com/c2kernel/gui/graph/controller/MultiSelectionDragController.java571
-rw-r--r--src/main/java/com/c2kernel/gui/graph/controller/StartVertexController.java79
-rw-r--r--src/main/java/com/c2kernel/gui/graph/controller/VertexConstructionController.java47
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/DefaultDirectedEdgeRenderer.java75
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/DefaultVertexRenderer.java60
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/DirectedEdgeRenderer.java11
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/EditorModeListener.java8
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/EditorPanel.java104
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/EditorToolBar.java346
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/GraphPanel.java272
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/PropertyTable.java40
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/PropertyTableModel.java135
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/SelectedVertexPanel.java27
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/VertexPropertyPanel.java259
-rw-r--r--src/main/java/com/c2kernel/gui/graph/view/VertexRenderer.java11
18 files changed, 2431 insertions, 0 deletions
diff --git a/src/main/java/com/c2kernel/gui/graph/controller/AutoScrollController.java b/src/main/java/com/c2kernel/gui/graph/controller/AutoScrollController.java
new file mode 100644
index 0000000..a09d6a2
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/controller/AutoScrollController.java
@@ -0,0 +1,41 @@
+package com.c2kernel.gui.graph.controller;
+
+import java.awt.Point;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.awt.event.MouseMotionListener;
+
+import com.c2kernel.gui.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/gui/graph/controller/DeletionController.java b/src/main/java/com/c2kernel/gui/graph/controller/DeletionController.java
new file mode 100644
index 0000000..4a8fb47
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/controller/DeletionController.java
@@ -0,0 +1,92 @@
+package com.c2kernel.gui.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/gui/graph/controller/EdgeConstructionController.java b/src/main/java/com/c2kernel/gui/graph/controller/EdgeConstructionController.java
new file mode 100644
index 0000000..f76d314
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/controller/EdgeConstructionController.java
@@ -0,0 +1,253 @@
+package com.c2kernel.gui.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.gui.graph.view.EditorModeListener;
+import com.c2kernel.gui.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/gui/graph/controller/MultiSelectionDragController.java b/src/main/java/com/c2kernel/gui/graph/controller/MultiSelectionDragController.java
new file mode 100644
index 0000000..2d77996
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/controller/MultiSelectionDragController.java
@@ -0,0 +1,571 @@
+package com.c2kernel.gui.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.gui.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<newOutline.length; i++)
+ {
+ if(mResizeInf.mOldOutline[i].x > 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/gui/graph/controller/StartVertexController.java b/src/main/java/com/c2kernel/gui/graph/controller/StartVertexController.java
new file mode 100644
index 0000000..e973132
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/controller/StartVertexController.java
@@ -0,0 +1,79 @@
+package com.c2kernel.gui.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/gui/graph/controller/VertexConstructionController.java b/src/main/java/com/c2kernel/gui/graph/controller/VertexConstructionController.java
new file mode 100644
index 0000000..cd6d455
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/controller/VertexConstructionController.java
@@ -0,0 +1,47 @@
+package com.c2kernel.gui.graph.controller;
+
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+
+import com.c2kernel.graph.model.GraphModelManager;
+import com.c2kernel.gui.graph.view.EditorModeListener;
+import com.c2kernel.gui.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/gui/graph/view/DefaultDirectedEdgeRenderer.java b/src/main/java/com/c2kernel/gui/graph/view/DefaultDirectedEdgeRenderer.java
new file mode 100644
index 0000000..e0d02c0
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/DefaultDirectedEdgeRenderer.java
@@ -0,0 +1,75 @@
+package com.c2kernel.gui.graph.view;
+
+import java.awt.Graphics2D;
+import java.awt.Paint;
+import java.awt.Shape;
+import java.awt.geom.AffineTransform;
+import java.awt.geom.GeneralPath;
+
+import com.c2kernel.graph.model.DirectedEdge;
+import com.c2kernel.graph.model.GraphPoint;
+
+
+public class DefaultDirectedEdgeRenderer implements DirectedEdgeRenderer
+{
+ private GeneralPath mArrowTemplate = new GeneralPath();
+ private Paint mLinePaint = null;
+
+
+ public DefaultDirectedEdgeRenderer(Paint linePaint)
+ {
+ mLinePaint = linePaint;
+
+ mArrowTemplate.moveTo(-5, 5);
+ mArrowTemplate.lineTo( 0, 0);
+ mArrowTemplate.lineTo( 5, 5);
+ }
+
+
+ @Override
+ public void draw(Graphics2D g2d, DirectedEdge directedEdge)
+ {
+ GraphPoint originPoint = directedEdge.getOriginPoint();
+ GraphPoint terminusPoint = directedEdge.getTerminusPoint();
+ GraphPoint midPoint = new GraphPoint();
+ AffineTransform transform = new AffineTransform();
+ Shape arrow = null;
+
+ g2d.setPaint(mLinePaint);
+ g2d.drawLine(originPoint.x, originPoint.y, terminusPoint.x, terminusPoint.y);
+
+ midPoint.x = originPoint.x + (terminusPoint.x - originPoint.x)/2;
+ midPoint.y = originPoint.y + (terminusPoint.y - originPoint.y)/2;
+
+ transform.translate(midPoint.x, midPoint.y);
+ transform.rotate(calcArrowAngle(originPoint.x, originPoint.y, terminusPoint.x, terminusPoint.y));
+
+ arrow = mArrowTemplate.createTransformedShape(transform);
+
+ g2d.draw(arrow);
+ }
+
+
+ private static double calcArrowAngle(int originX, int originY, int terminusX, int terminusY) {
+ double width = terminusX - originX;
+ double height = terminusY - originY;
+
+ if((width == 0) && (height > 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/gui/graph/view/DefaultVertexRenderer.java b/src/main/java/com/c2kernel/gui/graph/view/DefaultVertexRenderer.java
new file mode 100644
index 0000000..c8804e2
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/DefaultVertexRenderer.java
@@ -0,0 +1,60 @@
+package com.c2kernel.gui.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<outlinePoints.length; i++)
+ {
+ outline.addPoint(outlinePoints[i].x, outlinePoints[i].y);
+ }
+
+ // Fill and then draw the outline
+ g2d.setPaint(mFillPaint);
+ g2d.fill(outline);
+ g2d.setPaint(mLinePaint);
+ g2d.draw(outline);
+
+ // Write the name of the vertex in the centre of the outline
+ g2d.setPaint(mTextPaint);
+ g2d.drawString(vertexName, textX, textY);
+ }
+}
diff --git a/src/main/java/com/c2kernel/gui/graph/view/DirectedEdgeRenderer.java b/src/main/java/com/c2kernel/gui/graph/view/DirectedEdgeRenderer.java
new file mode 100644
index 0000000..32d9d92
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/DirectedEdgeRenderer.java
@@ -0,0 +1,11 @@
+package com.c2kernel.gui.graph.view;
+
+import java.awt.Graphics2D;
+
+import com.c2kernel.graph.model.DirectedEdge;
+
+
+public interface DirectedEdgeRenderer
+{
+ public void draw(Graphics2D g2d, DirectedEdge directedEdge);
+}
diff --git a/src/main/java/com/c2kernel/gui/graph/view/EditorModeListener.java b/src/main/java/com/c2kernel/gui/graph/view/EditorModeListener.java
new file mode 100644
index 0000000..c23f37e
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/EditorModeListener.java
@@ -0,0 +1,8 @@
+package com.c2kernel.gui.graph.view;
+
+
+
+public interface EditorModeListener
+{
+ public void editorModeChanged(String idOfNewMode);
+}
diff --git a/src/main/java/com/c2kernel/gui/graph/view/EditorPanel.java b/src/main/java/com/c2kernel/gui/graph/view/EditorPanel.java
new file mode 100644
index 0000000..1feb691
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/EditorPanel.java
@@ -0,0 +1,104 @@
+package com.c2kernel.gui.graph.view;
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+
+import com.c2kernel.graph.model.EdgeFactory;
+import com.c2kernel.graph.model.GraphModelManager;
+import com.c2kernel.graph.model.TypeNameAndConstructionInfo;
+import com.c2kernel.graph.model.VertexFactory;
+import com.c2kernel.graph.model.VertexOutlineCreator;
+import com.c2kernel.gui.graph.controller.AutoScrollController;
+import com.c2kernel.gui.graph.controller.EdgeConstructionController;
+import com.c2kernel.gui.graph.controller.MultiSelectionDragController;
+import com.c2kernel.gui.graph.controller.VertexConstructionController;
+public class EditorPanel extends JPanel
+{
+ // Graph Model
+ public final GraphModelManager mGraphModelManager = new GraphModelManager();
+ // Graph View
+ public GraphPanel mGraphPanel = null;
+ protected JScrollPane mGraphScrollPane = null;
+ // Graph Controllers
+ protected MultiSelectionDragController mMultiSelectionDragController = new MultiSelectionDragController();
+ protected VertexConstructionController mVertexConstructionController = new VertexConstructionController();
+ protected EdgeConstructionController mEdgeConstructionController = new EdgeConstructionController();
+ protected AutoScrollController mAutoScrollController = new AutoScrollController();
+ // Tool bar
+ protected EditorToolBar mEditorToolBar = null;
+ protected EditorPanel()
+ {
+ }
+ public EditorPanel(EdgeFactory eFactory, VertexFactory vFactory, VertexOutlineCreator vOutlineCreator, boolean edgeCreationMode, // True
+ // if
+ // edges
+ // can
+ // be
+ // created
+ JButton[] otherButtons, GraphPanel graphPanel)
+ {
+ // Create the graph panel and editor tool bar
+ setDoubleBuffered(true);
+ mGraphPanel = graphPanel;
+ mGraphPanel.setGraphModelManager(mGraphModelManager);
+ mGraphScrollPane = new JScrollPane(mGraphPanel);
+ mGraphModelManager.setExternalEdgeFactory(eFactory);
+ mGraphModelManager.setExternalVertexFactory(vFactory);
+ mGraphModelManager.setVertexOutlineCreator(vOutlineCreator);
+ mEditorToolBar = new EditorToolBar(edgeCreationMode, otherButtons, graphPanel);
+ mEditorToolBar.setGraphModelManager(mGraphModelManager);
+ mEditorToolBar.setGraphPanel(mGraphPanel);
+ createLayout();
+ // The graph panel observes the graph model
+ mGraphModelManager.addObserver(mGraphPanel);
+ // The multi selection drag controller modifies the graph model
+ // and listens to the graph panel and editor tool bar
+ mMultiSelectionDragController.setGraphModelManager(mGraphModelManager);
+ mGraphPanel.addMouseListener(mMultiSelectionDragController);
+ mGraphPanel.addMouseMotionListener(mMultiSelectionDragController);
+ mGraphPanel.addKeyListener(mMultiSelectionDragController);
+ mEditorToolBar.addEditorModeListener(mMultiSelectionDragController);
+ // The edge construction controller modifies the graph model
+ // and listens to the graph panel and editor tool bar
+ mEdgeConstructionController.setGraphModelManager(mGraphModelManager);
+ mGraphPanel.addMouseListener(mEdgeConstructionController);
+ mGraphPanel.addMouseMotionListener(mEdgeConstructionController);
+ mEdgeConstructionController.setEditorToolBar(mEditorToolBar);
+ // The vertex construction controller modifies the graph model
+ // and listens to the graph panel and editor tool bar
+ mVertexConstructionController.setGraphModelManager(mGraphModelManager);
+ mGraphPanel.addMouseListener(mVertexConstructionController);
+ mVertexConstructionController.setEditorToolBar(mEditorToolBar);
+ // The auto scroll controller listens to and modifies the
+ // graph panel
+ mAutoScrollController.setGraphPanel(mGraphPanel);
+ }
+
+ protected void createLayout()
+ {
+ setLayout(new BorderLayout());
+ add(mEditorToolBar, BorderLayout.NORTH);
+ mGraphPanel.setPreferredSize(new Dimension(mGraphModelManager.getModel().getWidth(), mGraphModelManager.getModel().getHeight()));
+ add(mGraphScrollPane, BorderLayout.CENTER);
+ }
+ public void enterSelectMode()
+ {
+ mEditorToolBar.enterSelectMode();
+ }
+ public void updateEdgeTypes(TypeNameAndConstructionInfo[] typeNameAndConstructionInfo)
+ {
+ mEditorToolBar.updateEdgeTypes(typeNameAndConstructionInfo);
+ }
+ public void updateVertexTypes(TypeNameAndConstructionInfo[] typeNameAndConstructionInfo)
+ {
+ mEditorToolBar.updateVertexTypes(typeNameAndConstructionInfo);
+ }
+ public void setEditable(boolean editable)
+ {
+ mGraphModelManager.setEditable(editable);
+ mEditorToolBar.setGraphEditable(editable);
+ }
+}
diff --git a/src/main/java/com/c2kernel/gui/graph/view/EditorToolBar.java b/src/main/java/com/c2kernel/gui/graph/view/EditorToolBar.java
new file mode 100644
index 0000000..deeaebb
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/EditorToolBar.java
@@ -0,0 +1,346 @@
+package com.c2kernel.gui.graph.view;
+import java.awt.Dimension;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.Image;
+import java.awt.Insets;
+import java.awt.Toolkit;
+import java.awt.datatransfer.Clipboard;
+import java.awt.datatransfer.ClipboardOwner;
+import java.awt.datatransfer.DataFlavor;
+import java.awt.datatransfer.Transferable;
+import java.awt.datatransfer.UnsupportedFlavorException;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.print.PageFormat;
+import java.awt.print.Printable;
+import java.awt.print.PrinterException;
+import java.awt.print.PrinterJob;
+import java.util.Vector;
+
+import javax.swing.Box;
+import javax.swing.BoxLayout;
+import javax.swing.ButtonGroup;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JToggleButton;
+
+import com.c2kernel.graph.model.GraphModelManager;
+import com.c2kernel.graph.model.TypeNameAndConstructionInfo;
+import com.c2kernel.gui.graph.controller.DeletionController;
+import com.c2kernel.gui.graph.controller.StartVertexController;
+import com.c2kernel.utils.Language;
+import com.c2kernel.utils.Logger;
+import com.c2kernel.utils.Resource;
+/**
+ * Tool bar with mode toggle buttons, start and delete buttons, and the possibility to add other arbitrary buttons at constructions time.
+ */
+public class EditorToolBar extends Box implements Printable
+{
+ protected boolean mEdgeCreationMode = false; // True if edges can be created
+ protected GraphPanel mGraphPanel = null;
+ // There is on mode button listener per mode button.
+ // When a mode button fires anaction performed event
+ // its corresponding listener notifies all of the
+ // editor mode listeners.
+ protected class ModeButtonListener implements ActionListener
+ {
+ protected String mModeId = null;
+ public ModeButtonListener(String modeId)
+ {
+ mModeId = modeId;
+ }
+ @Override
+ public void actionPerformed(ActionEvent ae)
+ {
+ notifyListeners(mModeId);
+ }
+ }
+ // Vertex types and ids
+ protected JComboBox mVertexTypeBox = new JComboBox();
+ // Edge types and ids
+ protected JComboBox mEdgeTypeBox = new JComboBox();
+ // Mode buttons
+ protected ButtonGroup mModeButtonGroup = new ButtonGroup();
+ protected JToggleButton mVertexModeButton = new JToggleButton(Resource.findImage("graph/newvertex.png"));
+ protected JToggleButton mSelectModeButton = new JToggleButton(Resource.findImage("graph/selection.gif"));
+ protected JToggleButton mEdgeModeButton = new JToggleButton(Resource.findImage("graph/edge.png"));
+ // Normal buttons
+ protected JButton[] mOtherButtons = null;
+ protected JButton mStartButton = new JButton(Resource.findImage("graph/start.png"));
+ protected JButton mDeleteButton = new JButton(Resource.findImage("graph/delete.png"));
+ protected JButton mPrintButton = new JButton(Resource.findImage("graph/print.png"));
+ protected JButton mCopyButton = new JButton(Resource.findImage("graph/copy.png"));
+ // Controllers
+ protected StartVertexController mStartVertexController = new StartVertexController();
+ protected DeletionController mDeletionController = new DeletionController();
+ // Editor mode listeners
+ protected Vector<EditorModeListener> mListenerVector = new Vector<EditorModeListener>(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/gui/graph/view/GraphPanel.java b/src/main/java/com/c2kernel/gui/graph/view/GraphPanel.java
new file mode 100644
index 0000000..fa7ed6f
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/GraphPanel.java
@@ -0,0 +1,272 @@
+package com.c2kernel.gui.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/gui/graph/view/PropertyTable.java b/src/main/java/com/c2kernel/gui/graph/view/PropertyTable.java
new file mode 100644
index 0000000..3776f86
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/PropertyTable.java
@@ -0,0 +1,40 @@
+package com.c2kernel.gui.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/gui/graph/view/PropertyTableModel.java b/src/main/java/com/c2kernel/gui/graph/view/PropertyTableModel.java
new file mode 100644
index 0000000..ce408e8
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/PropertyTableModel.java
@@ -0,0 +1,135 @@
+package com.c2kernel.gui.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 final String[] mColumnNames = { Language.translate("Name"), Language.translate("Value") };
+ HashMap<String, Object> sourceMap = new HashMap<String, Object>();
+ ArrayList<String> sortedNameList = new ArrayList<String>();
+ 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<? extends Object> oldElement = sourceMap.get(rowName).getClass();
+ if (oldElement == Double.class && value.getClass() == String.class)
+ try {
+ value = Double.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<String, Object> props) {
+ synchronized (sourceMap) {
+ sourceMap = props;
+ sortedNameList = new ArrayList<String>(props.size());
+ for (String string : props.keySet())
+ sortedNameList.add(string);
+
+ Collections.sort(sortedNameList, new Comparator<String>() {
+ @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/gui/graph/view/SelectedVertexPanel.java b/src/main/java/com/c2kernel/gui/graph/view/SelectedVertexPanel.java
new file mode 100644
index 0000000..282ce09
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/SelectedVertexPanel.java
@@ -0,0 +1,27 @@
+package com.c2kernel.gui.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/gui/graph/view/VertexPropertyPanel.java b/src/main/java/com/c2kernel/gui/graph/view/VertexPropertyPanel.java
new file mode 100644
index 0000000..c4b6303
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/VertexPropertyPanel.java
@@ -0,0 +1,259 @@
+package com.c2kernel.gui.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<String, Object>());
+ 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/gui/graph/view/VertexRenderer.java b/src/main/java/com/c2kernel/gui/graph/view/VertexRenderer.java
new file mode 100644
index 0000000..9757b0f
--- /dev/null
+++ b/src/main/java/com/c2kernel/gui/graph/view/VertexRenderer.java
@@ -0,0 +1,11 @@
+package com.c2kernel.gui.graph.view;
+
+import java.awt.Graphics2D;
+
+import com.c2kernel.graph.model.Vertex;
+
+
+public interface VertexRenderer
+{
+ public void draw(Graphics2D g2d, Vertex vertex);
+}