From: lempinen Date: Thu, 15 Dec 2011 08:42:25 +0000 (+0000) Subject: Chart elements for diagrams X-Git-Tag: simantics-1.6~69 X-Git-Url: https://gerrit.simantics.org/r/gitweb?a=commitdiff_plain;h=101eea014f800c8c712f7661698a78f0bf57c004;p=simantics%2Fsysdyn.git Chart elements for diagrams git-svn-id: https://www.simantics.org/svn/simantics/sysdyn/trunk@23568 ac1ea38d-2e2b-0410-8846-a27921b304fc --- diff --git a/org.simantics.sysdyn.ui/adapters.xml b/org.simantics.sysdyn.ui/adapters.xml index 336f9268..390ea2d9 100644 --- a/org.simantics.sysdyn.ui/adapters.xml +++ b/org.simantics.sysdyn.ui/adapters.xml @@ -10,5 +10,5 @@ VTT Technical Research Centre of Finland - initial API and implementation --> - none 0 fill 1 + none 0 fill 1 \ No newline at end of file diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java index e6c5107a..34dd82e5 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/editor/DiagramViewer.java @@ -48,7 +48,6 @@ import org.simantics.g2d.participant.GridPainter; import org.simantics.g2d.participant.RulerPainter; import org.simantics.modeling.mapping.ElementCopyAdvisor; import org.simantics.modeling.mapping.MappedElementCopyAdvisor; -import org.simantics.modeling.ui.diagramEditor.PopulateElementMonitorDropParticipant; import org.simantics.modeling.ui.diagramEditor.handlers.LinkBrowsingHandler; import org.simantics.structural2.modelingRules.IModelingRules; import org.simantics.sysdyn.SysdynResource; @@ -63,6 +62,7 @@ import org.simantics.sysdyn.ui.elements2.connections.ConnectionClasses; import org.simantics.sysdyn.ui.elements2.connections.RouteFlowEdgeClass; import org.simantics.sysdyn.ui.elements2.connections.SysdynConnectionClass; import org.simantics.sysdyn.ui.properties.SysdynPropertyPage; +import org.simantics.sysdyn.ui.trend.chart.element.PopulateChartDropParticipant; import org.simantics.ui.workbench.IPropertyPage; import org.simantics.utils.datastructures.hints.HintContext; import org.simantics.utils.datastructures.hints.IHintContext; @@ -206,7 +206,7 @@ public class DiagramViewer extends org.simantics.modeling.ui.diagramEditor.Diagr ctx.getDefaultHintContext().setHint(Hints.KEY_ALLOWED_DRAG_ACTIONS, DnDConstants.ACTION_COPY); ctx.add(new SysdynPopulateElementDropParticipant(synchronizer)); - ctx.add(new PopulateElementMonitorDropParticipant(synchronizer, 0.5, 0.5)); + ctx.add(new PopulateChartDropParticipant(synchronizer)); } @Override diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java index 7b8ac685..6ba81d70 100644 --- a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/properties/ResourceSelectionProcessor.java @@ -315,6 +315,11 @@ public class ResourceSelectionProcessor implements SelectionProcessor implements ISelectionPainterNode { + + private static final long serialVersionUID = 5013911689968911010L; + private boolean hover = false; + private boolean dragging = false; + + private ResizeListener resizeListener; + + protected transient JFreeChart chart = null; + + public boolean dragging() { + return dragging; + } + + public void setChart(JFreeChart chart) { + this.chart = chart; + } + + public void setResizeListener(ResizeListener listener) { + this.resizeListener = listener; + } + + @SyncField({"hover"}) + public void setHover(boolean hover) { + this.hover = hover; + repaint(); + } + + @Override + public void cleanup() { + removeEventHandler(this); + super.cleanup(); + } + + @Override + public void init() { + super.init(); + } + + public static void expand(Rectangle2D rectangle, double scaleX, double scaleY) { + GeometryUtils.expandRectangle(rectangle, + 5 * scaleY > 5 ? 5 * scaleY : 5, + 3 * scaleY > 3 ? 3 * scaleY : 3, + 3 * scaleX > 3 ? 3 * scaleX : 3, + 3 * scaleX > 3 ? 3 * scaleX : 3); + } + + @Override + public void render(Graphics2D g2d) { + if (component == null && chart != null) { + // Need the chart, so this cannot be done during initialization + component = new ChartPanel(chart, false); + ((ChartPanel)component).setRefreshBuffer(false); + component.setIgnoreRepaint(true); + component.setDoubleBuffered(false); + if(bounds != null) { + component.setBounds(0, 0, 0, 0); + } + super.init(); + addEventHandler(this); + } + + if (component != null) { + AffineTransform ot = g2d.getTransform(); + g2d.transform(transform); + double scaleX = g2d.getTransform().getScaleX(); + double scaleY = g2d.getTransform().getScaleY(); + + AffineTransform at = new AffineTransform(transform); + synchronized(transform) { + at.setToTranslation(bounds.getMinX(), bounds.getMinY()); + at.scale(1/scaleX, 1/scaleY); + } + g2d.transform(at); + int width = (int)(bounds.getWidth() * scaleX); + int height = (int)(bounds.getHeight() * scaleY); + + if(hover || dragging) { + // Borders + Color orig = g2d.getColor(); + BasicStroke oldStroke = (BasicStroke) g2d.getStroke(); + g2d.setColor(Color.LIGHT_GRAY); + Rectangle2D b = new Rectangle2D.Double(0, 0, width, height); + Rectangle2D r = b.getBounds2D(); + expand(r, 1 * scaleX, 1 * scaleY); + g2d.fill(r); + + // Lower right corner for dragging + double x = r.getMaxX() - (0.33 * scaleX); + double y = r.getMaxY() - (0.33 * scaleY); + + if(r.getMaxX() - x > 0.4) { + // if there is enough space, use decorated corner + BasicStroke hilightStroke = new BasicStroke(oldStroke.getLineWidth() * 3); + for(int i = 1; i < 4; i++) { + Line2D line = new Line2D.Double( + x - (i * scaleX), + y, + x, + y - (i * scaleY)); + + g2d.setStroke(hilightStroke); + g2d.setColor(Color.GRAY); + g2d.draw(line); + + g2d.setStroke(oldStroke); + g2d.setColor(Color.BLACK); + g2d.draw(line); + } + + + } else { + // not enough space, use a non-decorated corner + float f = 3; + Rectangle2D corner = new Rectangle2D.Double(r.getMaxX() - f, r.getMaxY() - f, f, f); + g2d.setColor(Color.DARK_GRAY); + g2d.fill(corner); + } + + g2d.setStroke(oldStroke); + g2d.setColor(orig); + } + + boolean selected = NodeUtil.isSelected(this, 1); + if (selected) { + Color orig = g2d.getColor(); + Stroke origStroke = g2d.getStroke(); + + g2d.setColor(Color.RED); + double s = GeometryUtils.getScale(g2d.getTransform()); + g2d.setStroke(new BasicStroke(1f * s < 1f ? 1f : 1f * (float)s)); + + Rectangle2D b = new Rectangle2D.Double(0, 0, width, height); + Rectangle2D r = b.getBounds2D(); + expand(r, scaleX, scaleY); + g2d.draw(r); + + g2d.setColor(orig); + g2d.setStroke(origStroke); + } + + synchronized(component) { + component.setLocation((int)g2d.getTransform().getTranslateX(), (int)g2d.getTransform().getTranslateY()); + if(component.getSize().getWidth() != width || component.getSize().getHeight() != height) { + component.setSize(width, height); + } + component.paint(g2d); + } + + g2d.setTransform(ot); + + + } + } + + @Override + protected boolean mouseButtonPressed(MouseButtonPressedEvent event) { + Point2D local = controlToLocal( event.controlPosition ); + local = parentToLocal(local); + Rectangle2D bounds = getBoundsInLocal().getBounds2D(); + expand(bounds, 1, 1); + double control = 3.0; + Rectangle2D corner = new Rectangle2D.Double(bounds.getMaxX() - control, bounds.getMaxY() - control, control, control); + if (hover && corner.contains(local)) { + dragging = true; + return true; + } + return super.mouseButtonPressed(event); + } + + + @Override + protected boolean mouseMoved(MouseMovedEvent e) { + if(dragging) { + Point2D local = controlToLocal( e.controlPosition ); + local = parentToLocal(local); + + Rectangle2D bounds = getBoundsInLocal().getBounds2D(); + + if(Math.abs(bounds.getMaxX() - local.getX()) > 3 || + Math.abs(bounds.getMaxY() - local.getY()) > 3) { + resize(local); + } + return true; + } + return false; + } + + public boolean hitCorner(Point2D point) { + Rectangle2D bounds = getBoundsInLocal().getBounds2D(); + expand(bounds, 1, 1); + double control = 3.0; + Rectangle2D corner = new Rectangle2D.Double(bounds.getMaxX() - control, bounds.getMaxY() - control, control, control); + return corner.contains(point); + } + + private void resize(Point2D local) { + Rectangle2D bounds = getBoundsInLocal().getBounds2D(); + double x = bounds.getX(); + double y = bounds.getY(); + double dx = local.getX() - bounds.getMaxX(); + double dy = local.getY() - bounds.getMaxY(); + double w = bounds.getWidth() + dx; + double h = bounds.getHeight() + dy; + bounds.setRect( + x, + y, + w > 20 ? w : 20, + h > 20 ? h : 20 + ); + setBounds(bounds); + } + + @Override + protected boolean mouseButtonReleased(MouseButtonReleasedEvent e) { + if(dragging) { + Point2D local = controlToLocal( e.controlPosition ); + local = parentToLocal(local); + if(Math.abs(bounds.getMaxX() - local.getX()) > 3 || + Math.abs(bounds.getMaxY() - local.getY()) > 3) { + resize(local); + } + dragging = false; + if(resizeListener != null) + resizeListener.elementResized(bounds); + } + return false; + } + + @Override + public int getEventMask() { + return EventTypes.MouseButtonPressedMask + | EventTypes.MouseMovedMask + | EventTypes.MouseButtonReleasedMask + ; + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/ChartSceneGraph.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/ChartSceneGraph.java new file mode 100644 index 00000000..f8ea0c72 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/ChartSceneGraph.java @@ -0,0 +1,159 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.trend.chart.element; + +import java.awt.Shape; +import java.awt.geom.AffineTransform; +import java.awt.geom.Rectangle2D; + +import org.jfree.chart.JFreeChart; +import org.simantics.g2d.canvas.ICanvasContext; +import org.simantics.g2d.diagram.DiagramUtils; +import org.simantics.g2d.diagram.IDiagram; +import org.simantics.g2d.element.ElementHints; +import org.simantics.g2d.element.ElementUtils; +import org.simantics.g2d.element.IElement; +import org.simantics.g2d.element.SceneGraphNodeKey; +import org.simantics.g2d.element.handler.HandleMouseEvent; +import org.simantics.g2d.element.handler.InternalSize; +import org.simantics.g2d.element.handler.PropertySetter; +import org.simantics.g2d.element.handler.SceneGraph; +import org.simantics.scenegraph.Node; +import org.simantics.scenegraph.g2d.G2DParentNode; +import org.simantics.scenegraph.g2d.IG2DNode; +import org.simantics.scenegraph.g2d.events.MouseEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseEnterEvent; +import org.simantics.scenegraph.g2d.events.MouseEvent.MouseExitEvent; +import org.simantics.utils.datastructures.hints.IHintContext.Key; +import org.simantics.utils.datastructures.hints.IHintListener; +import org.simantics.utils.datastructures.hints.IHintObservable; + +/** + * Chart scenegraph for chart elements in diagrams + * @author Teemu Lempinen + * + */ +public class ChartSceneGraph implements SceneGraph, HandleMouseEvent, InternalSize { + private static final long serialVersionUID = 1875762898776996989L; + + public static final Key KEY_SG_NODE = new SceneGraphNodeKey(Node.class, "CHART_SG_NODE"); + public static final Key KEY_SG_SELECTION_NODE = new SceneGraphNodeKey(Node.class, "CHART_SG_SELECTION_NODE"); + + protected IHintListener hoverHintListener; + + public ChartSceneGraph() { + } + + /** + * Expands bounds a little to allow selecting and grabbing a chart element + * from outside the chart graphics + */ + @Override + public Rectangle2D getBounds(IElement e, Rectangle2D s) { + if (s==null) s = new Rectangle2D.Double(); + + IG2DNode node = (IG2DNode)e.getHint(KEY_SG_NODE); + AffineTransform at = (AffineTransform)e.getHint(ElementHints.KEY_TRANSFORM); + if(at != null && node != null && node.getBoundsInLocal() != null) { + Shape shape = node.getBoundsInLocal(); + Rectangle2D r = shape.getBounds2D(); + double scaleX = at.getScaleX(); + double scaleY = at.getScaleY(); + ChartNode.expand(r, 1 * scaleX, 1 * scaleY); + shape = r; + s.setFrame(shape.getBounds2D()); + } else { + s.setFrame((Rectangle2D)e.getHint(ElementHints.KEY_BOUNDS)); + } + return s; + } + + @Override + public void cleanup(IElement e) { + if(hoverHintListener != null) + e.removeHintListener(hoverHintListener); + + Node node = e.removeHint(KEY_SG_NODE); + if (node != null) + node.remove(); + } + + @Override + public void init(final IElement e, G2DParentNode parent) { + ChartNode node = e.getHint(KEY_SG_NODE); + if(node == null) { + // Create a new chart node + node = parent.getOrCreateNode("chart_"+e.hashCode(), ChartNode.class); + + Rectangle2D bounds = (Rectangle2D)e.getHint(ElementHints.KEY_BOUNDS); + if(bounds == null) { + bounds = new Rectangle2D.Double(-40, -40, 80, 80); + e.setHint(ElementHints.KEY_BOUNDS, bounds); + } + node.setBounds(bounds); + + JFreeChart chart = e.getHint(ChartElementFactory.KEY_CHART); + if(chart != null) + node.setChart(chart); + + // Add a resize listener for updating bounds information to graph after resizing + node.setResizeListener(new ResizeListener() { + + @Override + public void elementResized(Rectangle2D newBounds) { + e.setHint(ElementHints.KEY_BOUNDS, newBounds); + IDiagram diagram = ElementUtils.getDiagram(e); + DiagramUtils.synchronizeHintsToBackend(diagram, e); + } + }); + + e.setHint(KEY_SG_NODE, node); + } + + // Hover listening + hoverHintListener = new IHintListener() { + @Override + public void hintRemoved(IHintObservable sender, Key key, Object oldValue) { + + } + + @Override + public void hintChanged(IHintObservable sender, Key key, Object oldValue, Object newValue) { + if(key == ElementHints.KEY_HOVER) { + IElement e = (IElement)sender; + ChartNode name = (ChartNode) e.getHint(KEY_SG_NODE); + if (name != null) + name.setHover(Boolean.TRUE.equals(e.getHint(ElementHints.KEY_HOVER))); + } + } + }; + e.addHintListener(hoverHintListener); + + update(e); + } + + public void update(IElement e) { + PropertySetter setter = e.getElementClass().getSingleItem(PropertySetter.class); + setter.syncPropertiesToNode(e); + } + + + @Override + public boolean handleMouseEvent(IElement e, ICanvasContext ctx, MouseEvent me) { + if (me instanceof MouseEnterEvent) { + e.setHint(ElementHints.KEY_HOVER, true); + } else if (me instanceof MouseExitEvent) { + e.setHint(ElementHints.KEY_HOVER, false); + } + return false; + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/PopulateChartDropParticipant.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/PopulateChartDropParticipant.java new file mode 100644 index 00000000..168c2a26 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/PopulateChartDropParticipant.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.trend.chart.element; + +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.geom.AffineTransform; +import java.io.IOException; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.simantics.db.ReadGraph; +import org.simantics.db.Resource; +import org.simantics.db.Session; +import org.simantics.db.common.request.UnaryRead; +import org.simantics.db.exception.DatabaseException; +import org.simantics.diagram.adapter.GraphToDiagramSynchronizer; +import org.simantics.g2d.dnd.DnDHints; +import org.simantics.g2d.dnd.ElementClassDragItem; +import org.simantics.g2d.dnd.IDnDContext; +import org.simantics.g2d.dnd.IDropTargetParticipant; +import org.simantics.g2d.element.ElementHints; +import org.simantics.modeling.ui.diagramEditor.PopulateElementDropParticipant; +import org.simantics.sysdyn.JFreeChartResource; +import org.simantics.ui.dnd.LocalObjectTransfer; +import org.simantics.ui.dnd.LocalObjectTransferable; +import org.simantics.ui.utils.AdaptionUtils; + +/** + * Drop participant for dropping chart definitions into diagrams + * @author Teemu Lempinen + * + */ +public class PopulateChartDropParticipant extends PopulateElementDropParticipant implements IDropTargetParticipant { + + public PopulateChartDropParticipant(GraphToDiagramSynchronizer synchronizer) { + super(synchronizer); + } + + @Override + public void dragEnter(DropTargetDragEvent dtde, final IDnDContext dp) { + Transferable tr = dtde.getTransferable(); + if (tr.isDataFlavorSupported(LocalObjectTransferable.FLAVOR)) { + + Session session = synchronizer.getSession(); + + Object obj = null; + + try { + obj = tr.getTransferData(LocalObjectTransferable.FLAVOR); + + // Create a structural selection from transfer data + if (!(obj instanceof IStructuredSelection)) { + obj = LocalObjectTransfer.getTransfer().getObject(); + } + + if (obj instanceof IStructuredSelection) { + + IStructuredSelection sel = (IStructuredSelection) obj; + + // Can drop only one chart at the time + if (sel.size() == 1) { + + // Get the chart definition + Resource chart = AdaptionUtils.adaptToSingle(sel, Resource.class); + if (chart == null) + return; + + ElementClassDragItem item = session.syncRequest(new UnaryRead(chart) { + + @Override + public ElementClassDragItem perform(ReadGraph graph) throws DatabaseException { + if(graph.isInstanceOf(parameter, JFreeChartResource.getInstance(graph).Chart)) { + ElementClassDragItem item = new ElementClassDragItem(ChartElementFactory.create(graph, parameter)); + AffineTransform initialTr = AffineTransform.getScaleInstance(1, 1); + item.getHintContext().setHint(ElementHints.KEY_TRANSFORM, initialTr); + return item; + } else { + return null; + } + + } + }); + + if(item != null) { + dp.add(item); + dp.getHints().setHint(DnDHints.KEY_DND_GRID_COLUMNS, Integer.valueOf(1)); + } + + } + + } + + } catch (UnsupportedFlavorException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } catch (DatabaseException e) { + e.printStackTrace(); + } + + } + + dtde.acceptDrag(DnDConstants.ACTION_COPY); + } +} diff --git a/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/ResizeListener.java b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/ResizeListener.java new file mode 100644 index 00000000..6c4116b5 --- /dev/null +++ b/org.simantics.sysdyn.ui/src/org/simantics/sysdyn/ui/trend/chart/element/ResizeListener.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2007, 2011 Association for Decentralized Information Management in + * Industry THTH ry. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * VTT Technical Research Centre of Finland - initial API and implementation + *******************************************************************************/ +package org.simantics.sysdyn.ui.trend.chart.element; + +import java.awt.geom.Rectangle2D; + +/** + * Interface for listeners listening resize events in chart nodes + * @author Teemu Lempinen + * + */ +public interface ResizeListener { + + /** + * Triggered when a node has been resized + * @param newBounds new bounds for the node + */ + public void elementResized(Rectangle2D newBounds); +}