1 /*******************************************************************************
2 * Copyright (c) 2007, 2010 Association for Decentralized Information Management
4 * All rights reserved. This program and the accompanying materials
5 * are made available under the terms of the Eclipse Public License v1.0
6 * which accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
10 * VTT Technical Research Centre of Finland - initial API and implementation
11 *******************************************************************************/
12 package org.simantics.g2d.dnd;
14 import java.awt.AlphaComposite;
15 import java.awt.geom.AffineTransform;
16 import java.awt.geom.Point2D;
17 import java.awt.geom.Rectangle2D;
19 import org.simantics.g2d.canvas.ICanvasContext;
20 import org.simantics.g2d.canvas.impl.AbstractCanvasParticipant;
21 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGCleanup;
22 import org.simantics.g2d.canvas.impl.SGNodeReflection.SGInit;
23 import org.simantics.g2d.diagram.DiagramHints;
24 import org.simantics.g2d.element.ElementHints;
25 import org.simantics.g2d.participant.TransformUtil;
26 import org.simantics.scenegraph.g2d.G2DParentNode;
27 import org.simantics.scenegraph.g2d.IG2DNode;
28 import org.simantics.scenegraph.g2d.events.EventHandlerReflection.EventHandler;
29 import org.simantics.scenegraph.g2d.events.command.CommandEvent;
30 import org.simantics.scenegraph.g2d.events.command.Commands;
31 import org.simantics.scenegraph.g2d.nodes.SingleElementNode;
32 import org.simantics.scenegraph.g2d.snap.ISnapAdvisor;
33 import org.simantics.utils.datastructures.context.IContext;
34 import org.simantics.utils.datastructures.context.IContextListener;
35 import org.slf4j.Logger;
36 import org.slf4j.LoggerFactory;
39 * This participant paints an array of icons of a drag operation.
41 * @author Toni Kalajainen
43 public class DragPainter extends AbstractCanvasParticipant {
44 private static final Logger LOGGER = LoggerFactory.getLogger(DragPainter.class);
45 // Priority of drag event, eats ecs key
46 public final static int DRAG_EVENT_PRIORITY = Integer.MAX_VALUE - 1500;
47 public final static int DRAG_PAINT_PRIORITY = Integer.MAX_VALUE - 1100;
48 public final static int MARGIN = 5;
50 final IDnDContext dropCtx;
51 Point2D mouseControlPos;
53 public DragPainter(IDnDContext dropCtx, Point2D mouseControlPos) {
55 this.dropCtx = dropCtx;
56 this.mouseControlPos = (Point2D) mouseControlPos.clone();
59 IContextListener<IDragItem> contextListener = new IContextListener<IDragItem>() {
61 public void itemAdded(IContext<IDragItem> sender, IDragItem item) {
62 updateItemPositions();
63 getContext().getContentContext().setDirty();
66 public void itemRemoved(IContext<IDragItem> sender, IDragItem item) {
67 updateItemPositions();
68 getContext().getContentContext().setDirty();
73 public void addedToContext(ICanvasContext ctx) {
74 super.addedToContext(ctx);
75 dropCtx.addContextListener(getContext().getThreadAccess(), contextListener);
79 public void removedFromContext(ICanvasContext ctx) {
80 dropCtx.removeContextListener(getContext().getThreadAccess(), contextListener);
81 super.removedFromContext(ctx);
84 public boolean setMousePos(Point2D mouseControlPos) {
85 return setMousePos(mouseControlPos, false);
88 public boolean setMousePos(Point2D mouseControlPos, boolean forceUpdate) {
89 if (!forceUpdate && mouseControlPos.equals(this.mouseControlPos))
91 this.mouseControlPos = (Point2D) mouseControlPos.clone();
92 getContext().getContentContext().setDirty();
93 updateItemPositions();
98 * Get the size of the largest drop item
102 private Point2D calcItemSize(IDragItem[] items)
104 double w = Double.MIN_VALUE;
105 double h = Double.MIN_VALUE;
106 for (IDragItem i : items) {
107 Rectangle2D bounds = i.getBounds();
108 if (bounds == null) bounds = new Rectangle2D.Double(0,0,1,1);
109 if (w < bounds.getWidth())
110 w = bounds.getWidth();
111 if (h < bounds.getHeight())
112 h = bounds.getHeight();
114 return new Point2D.Double(w, h);
117 private void updateItemPositions() {
118 // Calculate mouse position on diagram
119 TransformUtil vi = getContext().getSingleItem(TransformUtil.class);
120 AffineTransform controlToDiagram = vi.getInverseTransform();
121 if (controlToDiagram==null) return;
122 Point2D mouseDiagramPos = controlToDiagram.transform(mouseControlPos, new Point2D.Double());
124 //System.out.println("updateItemPositions: mousepos: " + mouseControlPos + " - " + mouseDiagramPos);
126 IDragItem items[] = dropCtx.toArray();
127 int count = items.length;
128 int columns = (int) Math.ceil( Math.sqrt( count) );
129 Integer columnsHint = dropCtx.getHints().getHint(DnDHints.KEY_DND_GRID_COLUMNS);
130 if (columnsHint != null)
131 columns = columnsHint;
132 int rows = (int) Math.ceil((double)count / (double)columns);
134 // Size of the largest item
135 Point2D size = calcItemSize(items);
137 // Calculate center point of the whole item mass
138 double cx = ((columns-1)*(size.getX()+margin))/2 + size.getX()/2;
139 double cy = ((rows -1)*(size.getY()+margin))/2 + size.getY()/2;
140 //double cx = ((columns)*(size.getX()+margin)-margin)/2 /*- size.getX()/2*/;
141 //double cy = ((rows )*(size.getY()+margin)-margin)/2 /*- size.getY()/2*/;
146 for (IDragItem i : items) {
147 Rectangle2D bounds = i.getBounds();
148 double x = (index % columns) * (margin + size.getX());
149 double y = (index / columns) * (margin + size.getY());
150 // Point2D pos = new Point2D.Double(x, y);
155 x += mouseDiagramPos.getX();
156 y += mouseDiagramPos.getY();
161 Point2D p = new Point2D.Double(x, y);
162 ISnapAdvisor snap = getHint(DiagramHints.SNAP_ADVISOR);
165 dropCtx.setItemPosition(i, p);
170 protected G2DParentNode node = null;
173 public void initSG(G2DParentNode parent) {
174 node = parent.addNode(G2DParentNode.class);
175 node.setZIndex(DRAG_PAINT_PRIORITY);
180 public void cleanupSG() {
185 private void updateSG() {
186 IDragItem items[] = dropCtx.toArray();
187 // updateItemPositions();
188 for (IDragItem item : items) {
189 Point2D pos = dropCtx.getItemPosition(item);
190 if(pos != null) { // Position can (or at least seems to be) be null on the first frame
191 AffineTransform subt = AffineTransform.getTranslateInstance(pos.getX(), pos.getY());
192 AffineTransform at = item.getHintContext().getHint(ElementHints.KEY_TRANSFORM);
194 subt.concatenate(at);
197 IG2DNode itemHolder = node.getNode(""+item.hashCode());
198 if (itemHolder != null && !(itemHolder instanceof SingleElementNode)) {
199 LOGGER.error("BUG: item hash codes collide within the dragged item context - found unrecognized item " + itemHolder + " with node id '" + item.hashCode() + "'");
203 SingleElementNode elementHolder = (SingleElementNode) itemHolder;
204 if (elementHolder == null) {
205 elementHolder = node.getOrCreateNode(""+item.hashCode(), SingleElementNode.class);
206 elementHolder.setTransform(subt);
207 elementHolder.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, 0.75f));
208 item.paint(elementHolder);
210 elementHolder.setTransform(subt);
216 @EventHandler(priority = DRAG_EVENT_PRIORITY)
217 public boolean handleEvent(CommandEvent e) {
218 if (e.command.equals( Commands.CANCEL) )